#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); } } ); namespace { template struct addControlFlow { addControlFlow() { function = [](T* _message, unsigned) { MutationInfo m{_message, "Added control flow."}; YPM::addControlFlow(_message); }; /// Unused variable registers callback. LPMPostProcessor callback(function); } function function; }; } static addControlFlow c1; static addControlFlow c2; static addControlFlow c3; static addControlFlow c4; static addControlFlow c5; static addControlFlow c6; static addControlFlow c7; static addControlFlow c8; 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, 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, 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, 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."); } template void YPM::addControlFlow(T* _msg) { enum class ControlFlowStmt: unsigned { For = 0, BoundedFor, If, Switch, FunctionCall, Break, Continue, Leave, Termination }; uniform_int_distribution d( static_cast(ControlFlowStmt::For), static_cast(ControlFlowStmt::Termination) ); auto random = static_cast(d(s_rand.m_random)); Statement* s = basicBlock(_msg)->add_statements(); switch (random) { case ControlFlowStmt::For: s->set_allocated_forstmt(new ForStmt()); break; case ControlFlowStmt::BoundedFor: s->set_allocated_boundedforstmt(new BoundedForStmt()); break; case ControlFlowStmt::If: s->set_allocated_ifstmt(new IfStmt()); break; case ControlFlowStmt::Switch: s->set_allocated_switchstmt(new SwitchStmt()); break; case ControlFlowStmt::FunctionCall: s->set_allocated_functioncall(new FunctionCall()); break; case ControlFlowStmt::Break: s->set_allocated_breakstmt(new BreakStmt()); break; case ControlFlowStmt::Continue: s->set_allocated_contstmt(new ContinueStmt()); break; case ControlFlowStmt::Leave: s->set_allocated_leave(new LeaveStmt()); break; case ControlFlowStmt::Termination: s->set_allocated_terminatestmt(new TerminatingStmt()); break; } } Block* YPM::randomBlock(ForStmt* _stmt) { enum class ForBlocks: unsigned { Init = 0, Post = 1, Body = 2 }; uniform_int_distribution d( static_cast(ForBlocks::Init), static_cast(ForBlocks::Body) ); switch (static_cast(d(s_rand.m_random))) { case ForBlocks::Init: return _stmt->mutable_for_init(); case ForBlocks::Post: return _stmt->mutable_for_post(); case ForBlocks::Body: return _stmt->mutable_for_body(); } } template Block* YPM::basicBlock(T* _msg) { if constexpr (std::is_same_v) return randomBlock(_msg); else if constexpr (std::is_same_v) return _msg->mutable_for_body(); else if constexpr (std::is_same_v) return _msg->mutable_default_block(); else if constexpr (std::is_same_v) return _msg->mutable_block(); else if constexpr (std::is_same_v) return _msg->mutable_if_body(); else if constexpr (std::is_same_v) return _msg->mutable_case_block(); else if constexpr (std::is_same_v) return _msg->mutable_block(); else if constexpr (std::is_same_v) return globalBlock(_msg); else static_assert(AlwaysFalse::value, "Yul proto mutator: non-exhaustive visitor."); } Block* YPM::globalBlock(Program* _program) { switch (_program->program_oneof_case()) { case Program::kBlock: return _program->mutable_block(); case Program::kObj: return _program->mutable_obj()->mutable_code()->mutable_block(); case Program::PROGRAM_ONEOF_NOT_SET: { _program->set_allocated_block(new Block()); return _program->mutable_block(); } } }