mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			267 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			267 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #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);
 | |
| 		}
 | |
| 	}
 | |
| );
 | |
| 
 | |
| 
 | |
| namespace
 | |
| {
 | |
| template <typename T>
 | |
| struct addControlFlow
 | |
| {
 | |
| 	addControlFlow()
 | |
| 	{
 | |
| 		function = [](T* _message, unsigned)
 | |
| 		{
 | |
| 			MutationInfo m{_message, "Added control flow."};
 | |
| 			YPM::addControlFlow(_message);
 | |
| 		};
 | |
| 		/// Unused variable registers callback.
 | |
| 		LPMPostProcessor<T> callback(function);
 | |
| 	}
 | |
| 	function<void(T*, unsigned)> function;
 | |
| };
 | |
| }
 | |
| 
 | |
| static addControlFlow<ForStmt> c1;
 | |
| static addControlFlow<BoundedForStmt> c2;
 | |
| static addControlFlow<IfStmt> c3;
 | |
| static addControlFlow<SwitchStmt> c4;
 | |
| static addControlFlow<FunctionDef> c5;
 | |
| static addControlFlow<CaseStmt> c6;
 | |
| static addControlFlow<Code> c7;
 | |
| 
 | |
| 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>, 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>, 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>, 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.");
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| void YPM::addControlFlow(T* _msg)
 | |
| {
 | |
| 	enum class ControlFlowStmt: unsigned
 | |
| 	{
 | |
| 		For = 0,
 | |
| 		BoundedFor,
 | |
| 		If,
 | |
| 		Switch,
 | |
| 		FunctionCall,
 | |
| 		Break,
 | |
| 		Continue,
 | |
| 		Leave,
 | |
| 		Termination
 | |
| 	};
 | |
| 	uniform_int_distribution<unsigned> d(
 | |
| 		static_cast<unsigned>(ControlFlowStmt::For),
 | |
| 		static_cast<unsigned>(ControlFlowStmt::Termination)
 | |
| 	);
 | |
| 	auto random = static_cast<ControlFlowStmt>(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<unsigned> d(
 | |
| 		static_cast<unsigned>(ForBlocks::Init),
 | |
| 		static_cast<unsigned>(ForBlocks::Body)
 | |
| 	);
 | |
| 	switch (static_cast<ForBlocks>(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 <typename T>
 | |
| Block* YPM::basicBlock(T* _msg)
 | |
| {
 | |
| 	if constexpr (std::is_same_v<T, ForStmt>)
 | |
| 		return randomBlock(_msg);
 | |
| 	else if constexpr (std::is_same_v<T, BoundedForStmt>)
 | |
| 		return _msg->mutable_for_body();
 | |
| 	else if constexpr (std::is_same_v<T, SwitchStmt>)
 | |
| 		return _msg->mutable_default_block();
 | |
| 	else if constexpr (std::is_same_v<T, FunctionDef>)
 | |
| 		return _msg->mutable_block();
 | |
| 	else if constexpr (std::is_same_v<T, IfStmt>)
 | |
| 		return _msg->mutable_if_body();
 | |
| 	else if constexpr (std::is_same_v<T, CaseStmt>)
 | |
| 		return _msg->mutable_case_block();
 | |
| 	else if constexpr (std::is_same_v<T, Code>)
 | |
| 		return _msg->mutable_block();
 | |
| 	else
 | |
| 		static_assert(AlwaysFalse<T>::value, "Yul proto mutator: non-exhaustive visitor.");
 | |
| }
 |