From 72721293548d89d089195e73c263efab01c5d27d Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Mon, 18 Nov 2019 12:12:30 +0100 Subject: [PATCH] yul fuzzer: Add framework for yul optimizer custom mutation routines Co-authored-by: Leonardo --- .circleci/config.yml | 1 + test/tools/ossfuzz/CMakeLists.txt | 23 ++- .../ossfuzz/protomutators/YulProtoMutator.cpp | 147 ++++++++++++++++++ .../ossfuzz/protomutators/YulProtoMutator.h | 98 ++++++++++++ 4 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 test/tools/ossfuzz/protomutators/YulProtoMutator.cpp create mode 100644 test/tools/ossfuzz/protomutators/YulProtoMutator.h diff --git a/.circleci/config.yml b/.circleci/config.yml index 5157f0661..d17f13baa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -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 diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index ccd71a983..8a33f1f7c 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -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 diff --git a/test/tools/ossfuzz/protomutators/YulProtoMutator.cpp b/test/tools/ossfuzz/protomutators/YulProtoMutator.cpp new file mode 100644 index 000000000..32fda663d --- /dev/null +++ b/test/tools/ossfuzz/protomutators/YulProtoMutator.cpp @@ -0,0 +1,147 @@ +#include + +#include + +#include + +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 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{}.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 +T YPM::EnumTypeConverter::validEnum(unsigned _seed) +{ + auto ret = static_cast(_seed % (enumMax() - enumMin() + 1) + enumMin()); + if constexpr (std::is_same_v, FunctionCall_Returns>) + yulAssert(FunctionCall_Returns_IsValid(ret), "Yul proto mutator: Invalid enum"); + else if constexpr (std::is_same_v, StoreFunc_Storage>) + yulAssert(StoreFunc_Storage_IsValid(ret), "Yul proto mutator: Invalid enum"); + else if constexpr (std::is_same_v, NullaryOp_NOp>) + yulAssert(NullaryOp_NOp_IsValid(ret), "Yul proto mutator: Invalid enum"); + else if constexpr (std::is_same_v, BinaryOp_BOp>) + yulAssert(BinaryOp_BOp_IsValid(ret), "Yul proto mutator: Invalid enum"); + else if constexpr (std::is_same_v, UnaryOp_UOp>) + yulAssert(UnaryOp_UOp_IsValid(ret), "Yul proto mutator: Invalid enum"); + else if constexpr (std::is_same_v, LowLevelCall_Type>) + yulAssert(LowLevelCall_Type_IsValid(ret), "Yul proto mutator: Invalid enum"); + else if constexpr (std::is_same_v, Create_Type>) + yulAssert(Create_Type_IsValid(ret), "Yul proto mutator: Invalid enum"); + else if constexpr (std::is_same_v, UnaryOpData_UOpData>) + yulAssert(UnaryOpData_UOpData_IsValid(ret), "Yul proto mutator: Invalid enum"); + else + static_assert(AlwaysFalse::value, "Yul proto mutator: non-exhaustive visitor."); + return ret; +} + +template +unsigned YPM::EnumTypeConverter::enumMax() +{ + if constexpr (std::is_same_v, FunctionCall_Returns>) + return FunctionCall_Returns_Returns_MAX; + else if constexpr (std::is_same_v, StoreFunc_Storage>) + return StoreFunc_Storage_Storage_MAX; + else if constexpr (std::is_same_v, NullaryOp_NOp>) + return NullaryOp_NOp_NOp_MAX; + else if constexpr (std::is_same_v, BinaryOp_BOp>) + return BinaryOp_BOp_BOp_MAX; + else if constexpr (std::is_same_v, UnaryOp_UOp>) + return UnaryOp_UOp_UOp_MAX; + else if constexpr (std::is_same_v, LowLevelCall_Type>) + return LowLevelCall_Type_Type_MAX; + else if constexpr (std::is_same_v, Create_Type>) + return Create_Type_Type_MAX; + else if constexpr (std::is_same_v, UnaryOpData_UOpData>) + return UnaryOpData_UOpData_UOpData_MAX; + else + static_assert(AlwaysFalse::value, "Yul proto mutator: non-exhaustive visitor."); +} + +template +unsigned YPM::EnumTypeConverter::enumMin() +{ + if constexpr (std::is_same_v, FunctionCall_Returns>) + return FunctionCall_Returns_Returns_MIN; + else if constexpr (std::is_same_v, StoreFunc_Storage>) + return StoreFunc_Storage_Storage_MIN; + else if constexpr (std::is_same_v, NullaryOp_NOp>) + return NullaryOp_NOp_NOp_MIN; + else if constexpr (std::is_same_v, BinaryOp_BOp>) + return BinaryOp_BOp_BOp_MIN; + else if constexpr (std::is_same_v, UnaryOp_UOp>) + return UnaryOp_UOp_UOp_MIN; + else if constexpr (std::is_same_v, LowLevelCall_Type>) + return LowLevelCall_Type_Type_MIN; + else if constexpr (std::is_same_v, Create_Type>) + return Create_Type_Type_MIN; + else if constexpr (std::is_same_v, UnaryOpData_UOpData>) + return UnaryOpData_UOpData_UOpData_MIN; + else + static_assert(AlwaysFalse::value, "Yul proto mutator: non-exhaustive visitor."); +} diff --git a/test/tools/ossfuzz/protomutators/YulProtoMutator.h b/test/tools/ossfuzz/protomutators/YulProtoMutator.h new file mode 100644 index 000000000..0627a3468 --- /dev/null +++ b/test/tools/ossfuzz/protomutators/YulProtoMutator.h @@ -0,0 +1,98 @@ +#pragma once + +#include + +#include + +#include + +#include + +namespace solidity::yul::test::yul_fuzzer +{ + +using ProtobufMessage = google::protobuf::Message; + +template +using LPMPostProcessor = protobuf_mutator::libfuzzer::PostProcessorRegistration; + +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 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 + 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; +}; +}