mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
yul fuzzer: Add framework for yul optimizer custom mutation routines
Co-authored-by: Leonardo <leo@ethereum.org>
This commit is contained in:
parent
8548bf1b4c
commit
7272129354
@ -97,6 +97,7 @@ defaults:
|
||||
- test/tools/ossfuzz/strictasm_diff_ossfuzz
|
||||
- test/tools/ossfuzz/strictasm_opt_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/sol_proto_ossfuzz
|
||||
|
||||
|
@ -10,7 +10,12 @@ add_dependencies(ossfuzz
|
||||
|
||||
if (OSSFUZZ)
|
||||
add_custom_target(ossfuzz_proto)
|
||||
add_dependencies(ossfuzz_proto yul_proto_ossfuzz yul_proto_diff_ossfuzz sol_proto_ossfuzz)
|
||||
add_dependencies(ossfuzz_proto
|
||||
sol_proto_ossfuzz
|
||||
yul_proto_ossfuzz
|
||||
yul_proto_diff_ossfuzz
|
||||
yul_proto_diff_custom_mutate_ossfuzz
|
||||
)
|
||||
|
||||
add_custom_target(ossfuzz_abiv2)
|
||||
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})
|
||||
|
||||
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
|
||||
../../EVMHost.cpp
|
||||
abiV2ProtoFuzzer.cpp
|
||||
|
147
test/tools/ossfuzz/protomutators/YulProtoMutator.cpp
Normal file
147
test/tools/ossfuzz/protomutators/YulProtoMutator.cpp
Normal file
@ -0,0 +1,147 @@
|
||||
#include <test/tools/ossfuzz/protomutators/YulProtoMutator.h>
|
||||
|
||||
#include <libyul/Exceptions.h>
|
||||
|
||||
#include <src/text_format.h>
|
||||
|
||||
using namespace solidity::yul::test::yul_fuzzer;
|
||||
using namespace protobuf_mutator;
|
||||
using namespace std;
|
||||
|
||||
using YPM = YulProtoMutator;
|
||||
|
||||
MutationInfo::MutationInfo(ProtobufMessage const* _message, string const& _info):
|
||||
ScopeGuard([&]{ exitInfo(); }),
|
||||
m_protobufMsg(_message)
|
||||
{
|
||||
writeLine("----------------------------------");
|
||||
writeLine("YULMUTATOR: " + _info);
|
||||
writeLine("Before");
|
||||
writeLine(SaveMessageAsText(*m_protobufMsg));
|
||||
}
|
||||
|
||||
void MutationInfo::exitInfo()
|
||||
{
|
||||
writeLine("After");
|
||||
writeLine(SaveMessageAsText(*m_protobufMsg));
|
||||
}
|
||||
|
||||
/// Initialize deterministic PRNG.
|
||||
static YulRandomNumGenerator s_rand(1337);
|
||||
|
||||
/// Add m/sstore(0, variable)
|
||||
static LPMPostProcessor<Block> addStoreToZero(
|
||||
[](Block* _message, unsigned _seed)
|
||||
{
|
||||
if (_seed % YPM::s_highIP == 0)
|
||||
{
|
||||
MutationInfo m{_message, "Added store to zero"};
|
||||
auto storeStmt = new StoreFunc();
|
||||
storeStmt->set_st(YPM::EnumTypeConverter<StoreFunc_Storage>{}.enumFromSeed(s_rand()));
|
||||
storeStmt->set_allocated_loc(YPM::litExpression(0));
|
||||
storeStmt->set_allocated_val(YPM::refExpression(s_rand));
|
||||
auto stmt = _message->add_statements();
|
||||
stmt->set_allocated_storage_func(storeStmt);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Literal* YPM::intLiteral(unsigned _value)
|
||||
{
|
||||
auto lit = new Literal();
|
||||
lit->set_intval(_value);
|
||||
return lit;
|
||||
}
|
||||
|
||||
VarRef* YPM::varRef(unsigned _seed)
|
||||
{
|
||||
auto varref = new VarRef();
|
||||
varref->set_varnum(_seed);
|
||||
return varref;
|
||||
}
|
||||
|
||||
Expression* YPM::refExpression(YulRandomNumGenerator& _rand)
|
||||
{
|
||||
auto refExpr = new Expression();
|
||||
refExpr->set_allocated_varref(varRef(_rand()));
|
||||
return refExpr;
|
||||
}
|
||||
|
||||
Expression* YPM::litExpression(unsigned _value)
|
||||
{
|
||||
auto lit = intLiteral(_value);
|
||||
auto expr = new Expression();
|
||||
expr->set_allocated_cons(lit);
|
||||
return expr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T YPM::EnumTypeConverter<T>::validEnum(unsigned _seed)
|
||||
{
|
||||
auto ret = static_cast<T>(_seed % (enumMax() - enumMin() + 1) + enumMin());
|
||||
if constexpr (std::is_same_v<std::decay_t<T>, FunctionCall_Returns>)
|
||||
yulAssert(FunctionCall_Returns_IsValid(ret), "Yul proto mutator: Invalid enum");
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, StoreFunc_Storage>)
|
||||
yulAssert(StoreFunc_Storage_IsValid(ret), "Yul proto mutator: Invalid enum");
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, NullaryOp_NOp>)
|
||||
yulAssert(NullaryOp_NOp_IsValid(ret), "Yul proto mutator: Invalid enum");
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, BinaryOp_BOp>)
|
||||
yulAssert(BinaryOp_BOp_IsValid(ret), "Yul proto mutator: Invalid enum");
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, UnaryOp_UOp>)
|
||||
yulAssert(UnaryOp_UOp_IsValid(ret), "Yul proto mutator: Invalid enum");
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, LowLevelCall_Type>)
|
||||
yulAssert(LowLevelCall_Type_IsValid(ret), "Yul proto mutator: Invalid enum");
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, Create_Type>)
|
||||
yulAssert(Create_Type_IsValid(ret), "Yul proto mutator: Invalid enum");
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, UnaryOpData_UOpData>)
|
||||
yulAssert(UnaryOpData_UOpData_IsValid(ret), "Yul proto mutator: Invalid enum");
|
||||
else
|
||||
static_assert(AlwaysFalse<T>::value, "Yul proto mutator: non-exhaustive visitor.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
unsigned YPM::EnumTypeConverter<T>::enumMax()
|
||||
{
|
||||
if constexpr (std::is_same_v<std::decay_t<T>, FunctionCall_Returns>)
|
||||
return FunctionCall_Returns_Returns_MAX;
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, StoreFunc_Storage>)
|
||||
return StoreFunc_Storage_Storage_MAX;
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, NullaryOp_NOp>)
|
||||
return NullaryOp_NOp_NOp_MAX;
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, BinaryOp_BOp>)
|
||||
return BinaryOp_BOp_BOp_MAX;
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, UnaryOp_UOp>)
|
||||
return UnaryOp_UOp_UOp_MAX;
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, LowLevelCall_Type>)
|
||||
return LowLevelCall_Type_Type_MAX;
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, Create_Type>)
|
||||
return Create_Type_Type_MAX;
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, UnaryOpData_UOpData>)
|
||||
return UnaryOpData_UOpData_UOpData_MAX;
|
||||
else
|
||||
static_assert(AlwaysFalse<T>::value, "Yul proto mutator: non-exhaustive visitor.");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
unsigned YPM::EnumTypeConverter<T>::enumMin()
|
||||
{
|
||||
if constexpr (std::is_same_v<std::decay_t<T>, FunctionCall_Returns>)
|
||||
return FunctionCall_Returns_Returns_MIN;
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, StoreFunc_Storage>)
|
||||
return StoreFunc_Storage_Storage_MIN;
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, NullaryOp_NOp>)
|
||||
return NullaryOp_NOp_NOp_MIN;
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, BinaryOp_BOp>)
|
||||
return BinaryOp_BOp_BOp_MIN;
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, UnaryOp_UOp>)
|
||||
return UnaryOp_UOp_UOp_MIN;
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, LowLevelCall_Type>)
|
||||
return LowLevelCall_Type_Type_MIN;
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, Create_Type>)
|
||||
return Create_Type_Type_MIN;
|
||||
else if constexpr (std::is_same_v<std::decay_t<T>, UnaryOpData_UOpData>)
|
||||
return UnaryOpData_UOpData_UOpData_MIN;
|
||||
else
|
||||
static_assert(AlwaysFalse<T>::value, "Yul proto mutator: non-exhaustive visitor.");
|
||||
}
|
98
test/tools/ossfuzz/protomutators/YulProtoMutator.h
Normal file
98
test/tools/ossfuzz/protomutators/YulProtoMutator.h
Normal file
@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include <test/tools/ossfuzz/yulProto.pb.h>
|
||||
|
||||
#include <libsolutil/Common.h>
|
||||
|
||||
#include <src/libfuzzer/libfuzzer_macro.h>
|
||||
|
||||
#include <random>
|
||||
|
||||
namespace solidity::yul::test::yul_fuzzer
|
||||
{
|
||||
|
||||
using ProtobufMessage = google::protobuf::Message;
|
||||
|
||||
template <typename Proto>
|
||||
using LPMPostProcessor = protobuf_mutator::libfuzzer::PostProcessorRegistration<Proto>;
|
||||
|
||||
class MutationInfo: public ScopeGuard
|
||||
{
|
||||
public:
|
||||
MutationInfo(ProtobufMessage const* _message, std::string const& _info);
|
||||
|
||||
static void writeLine(std::string const& _str)
|
||||
{
|
||||
std::cout << _str << std::endl;
|
||||
}
|
||||
void exitInfo();
|
||||
|
||||
ProtobufMessage const* m_protobufMsg;
|
||||
};
|
||||
|
||||
struct YulRandomNumGenerator
|
||||
{
|
||||
using RandomEngine = std::minstd_rand;
|
||||
|
||||
explicit YulRandomNumGenerator(unsigned _seed): m_random(RandomEngine(_seed)) {}
|
||||
|
||||
unsigned operator()()
|
||||
{
|
||||
return m_random();
|
||||
}
|
||||
|
||||
RandomEngine m_random;
|
||||
};
|
||||
|
||||
struct YulProtoMutator
|
||||
{
|
||||
/// @param _value: Value of the integer literal
|
||||
/// @returns an integer literal protobuf message initialized with
|
||||
/// the given value.
|
||||
static Literal* intLiteral(unsigned _value);
|
||||
|
||||
/// @param _seed: Pseudo-random unsigned integer used as index
|
||||
/// of variable to be referenced
|
||||
/// @returns a variable reference protobuf message.
|
||||
static VarRef* varRef(unsigned _seed);
|
||||
|
||||
/// @param _value: value of literal expression
|
||||
/// @returns an expression protobuf message
|
||||
static Expression* litExpression(unsigned _value);
|
||||
|
||||
/// @param _rand: Pseudo-random number generator
|
||||
/// of variable to be referenced
|
||||
/// @returns a variable reference protobuf message
|
||||
static Expression* refExpression(YulRandomNumGenerator& _rand);
|
||||
|
||||
/// Helper type for type matching visitor.
|
||||
template<class T> struct AlwaysFalse: std::false_type {};
|
||||
|
||||
/// Template struct for obtaining a valid enum value of
|
||||
/// template type from a pseudo-random unsigned integer.
|
||||
/// @param _seed: Pseudo-random integer
|
||||
/// @returns Valid enum of enum type T
|
||||
template <typename T>
|
||||
struct EnumTypeConverter
|
||||
{
|
||||
T enumFromSeed(unsigned _seed)
|
||||
{
|
||||
return validEnum(_seed);
|
||||
}
|
||||
|
||||
/// @returns a valid enum of type T from _seed
|
||||
T validEnum(unsigned _seed);
|
||||
/// @returns maximum enum value for enum of type T
|
||||
static unsigned enumMax();
|
||||
/// @returns minimum enum value for enum of type T
|
||||
static unsigned enumMin();
|
||||
};
|
||||
|
||||
/// Modulo for mutations that should occur rarely
|
||||
static constexpr unsigned s_lowIP = 31;
|
||||
/// Modulo for mutations that should occur not too often
|
||||
static constexpr unsigned s_mediumIP = 29;
|
||||
/// Modulo for mutations that should occur often
|
||||
static constexpr unsigned s_highIP = 23;
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user