mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			395 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			395 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| 	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/>.
 | |
| */
 | |
| // SPDX-License-Identifier: GPL-3.0
 | |
| 
 | |
| #include <cstdint>
 | |
| #include <cstddef>
 | |
| #include <string>
 | |
| #include <ostream>
 | |
| #include <sstream>
 | |
| #include <stack>
 | |
| #include <set>
 | |
| #include <vector>
 | |
| #include <tuple>
 | |
| 
 | |
| #include <test/tools/ossfuzz/yulProto.pb.h>
 | |
| 
 | |
| #include <libsolutil/Common.h>
 | |
| #include <libsolutil/FixedHash.h>
 | |
| #include <libsolutil/Whiskers.h>
 | |
| 
 | |
| #include <liblangutil/EVMVersion.h>
 | |
| 
 | |
| namespace solidity::yul::test::yul_fuzzer
 | |
| {
 | |
| class ProtoConverter
 | |
| {
 | |
| public:
 | |
| 	ProtoConverter()
 | |
| 	{
 | |
| 		m_funcVars = std::vector<std::vector<std::vector<std::string>>>{};
 | |
| 		m_globalVars = std::vector<std::vector<std::string>>{};
 | |
| 		m_inForBodyScope = false;
 | |
| 		m_inForInitScope = false;
 | |
| 		m_inForCond = false;
 | |
| 		m_numNestedForLoops = 0;
 | |
| 		m_numForLoops = 0;
 | |
| 		m_counter = 0;
 | |
| 		m_inputSize = 0;
 | |
| 		m_inFunctionDef = false;
 | |
| 		m_objectId = 0;
 | |
| 		m_isObject = false;
 | |
| 		m_forInitScopeExtEnabled = true;
 | |
| 	}
 | |
| 	ProtoConverter(ProtoConverter const&) = delete;
 | |
| 	ProtoConverter(ProtoConverter&&) = delete;
 | |
| 	std::string programToString(Program const& _input);
 | |
| 
 | |
| 	/// Returns evm version
 | |
| 	solidity::langutil::EVMVersion version()
 | |
| 	{
 | |
| 		return m_evmVersion;
 | |
| 	}
 | |
| 
 | |
| private:
 | |
| 	void visit(BinaryOp const&);
 | |
| 
 | |
| 	/// Visits a basic block optionally adding @a _funcParams to scope.
 | |
| 	/// @param _block Reference to a basic block of Yul statements.
 | |
| 	/// @param _funcParams List of function parameter names, defaults to
 | |
| 	/// an empty vector.
 | |
| 	void visit(Block const& _block);
 | |
| 
 | |
| 	std::string visit(Literal const&);
 | |
| 	void visit(VarRef const&);
 | |
| 	void visit(Expression const&);
 | |
| 	void visit(VarDecl const&);
 | |
| 	void visit(MultiVarDecl const&);
 | |
| 	void visit(TypedVarDecl const&);
 | |
| 	void visit(UnaryOp const&);
 | |
| 	void visit(AssignmentStatement const&);
 | |
| 	void visit(IfStmt const&);
 | |
| 	void visit(StoreFunc const&);
 | |
| 	void visit(Statement const&);
 | |
| 	void visit(ForStmt const&);
 | |
| 	void visit(BoundedForStmt const&);
 | |
| 	void visit(CaseStmt const&);
 | |
| 	void visit(SwitchStmt const&);
 | |
| 	void visit(TernaryOp 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(FunctionDef const&);
 | |
| 	void visit(PopStmt 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&);
 | |
| 
 | |
| 	/// Creates a new block scope.
 | |
| 	void openBlockScope();
 | |
| 	/// Creates a new function scope, and adds @a _funcParams to it if it
 | |
| 	/// is non-empty.
 | |
| 	void openFunctionScope(std::vector<std::string> const& _funcParams);
 | |
| 	/// Closes current block scope
 | |
| 	void closeBlockScope();
 | |
| 	/// Closes current function scope
 | |
| 	void closeFunctionScope();
 | |
| 	/// Adds @a _vars to current scope
 | |
| 	void addVarsToScope(std::vector<std::string> const& _vars);
 | |
| 	/// @returns number of variables that are in scope
 | |
| 	unsigned numVarsInScope();
 | |
| 
 | |
| 	std::string createHex(std::string const& _hexBytes);
 | |
| 
 | |
| 	/// Returns a new variable name.
 | |
| 	std::string newVarName()
 | |
| 	{
 | |
| 		return "x_" + std::to_string(counter());
 | |
| 	}
 | |
| 
 | |
| 	/// Accepts an arbitrary string, removes all characters that are neither
 | |
| 	/// alphabets nor digits from it and returns the said string.
 | |
| 	std::string createAlphaNum(std::string const& _strBytes);
 | |
| 
 | |
| 	enum class NumFunctionReturns
 | |
| 	{
 | |
| 		None,
 | |
| 		Single,
 | |
| 		Multiple
 | |
| 	};
 | |
| 
 | |
| 	void visitFunctionInputParams(FunctionCall const&, unsigned);
 | |
| 	void createFunctionDefAndCall(FunctionDef const&, unsigned, unsigned);
 | |
| 
 | |
| 	/// Convert function type to a string to be used while naming a
 | |
| 	/// function that is created by a function declaration statement.
 | |
| 	/// @param _type Type classified according to the number of
 | |
| 	/// values returned by function.
 | |
| 	/// @return A string as follows. If _type is
 | |
| 	/// None -> "n"
 | |
| 	/// Single -> "s"
 | |
| 	/// Multiple -> "m"
 | |
| 	std::string functionTypeToString(NumFunctionReturns _type);
 | |
| 
 | |
| 	/// Builds a single vector containing variables declared in
 | |
| 	/// function scope.
 | |
| 	void consolidateVarDeclsInFunctionDef();
 | |
| 
 | |
| 	/// Builds a single vector containing variables declared in
 | |
| 	/// global scope.
 | |
| 	void consolidateGlobalVarDecls();
 | |
| 
 | |
| 	/// Return true if at least one variable declaration is in scope,
 | |
| 	/// false otherwise.
 | |
| 	/// @return True in the following cases:
 | |
| 	/// - If we are inside a function that has already declared a variable
 | |
| 	/// - If there is at least one variable declaration that is
 | |
| 	/// in scope
 | |
| 	bool varDeclAvailable();
 | |
| 
 | |
| 	/// Return true if a function call cannot be made, false otherwise.
 | |
| 	/// @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.
 | |
| 	/// @param _x Protobuf function call
 | |
| 	/// @param _name Function name
 | |
| 	/// @param _numInParams Number of input arguments accepted by function
 | |
| 	/// @param _newLine Flag that prints a new line to the output stream if
 | |
| 	/// true. Default value for the flag is true.
 | |
| 	void convertFunctionCall(
 | |
| 		FunctionCall const& _x,
 | |
| 		std::string _name,
 | |
| 		unsigned _numInParams,
 | |
| 		bool _newLine = true
 | |
| 	);
 | |
| 
 | |
| 	/// Prints a Yul formatted variable declaration statement to the output
 | |
| 	/// stream.
 | |
| 	/// Example 1: createVarDecls(0, 1, true) returns {"x_0"} and prints
 | |
| 	///		let x_0 :=
 | |
| 	/// Example 2: createVarDecls(0, 2, false) returns {"x_0", "x_1"} and prints
 | |
| 	///		let x_0, x_1
 | |
| 	/// @param _start Start index of variable (inclusive)
 | |
| 	/// @param _end End index of variable (exclusive)
 | |
| 	/// @param _isAssignment Flag indicating if variable declaration is also
 | |
| 	/// an assignment. If true, the string " := " follows the variable
 | |
| 	/// declaration. Otherwise, a new line is follows the variable
 | |
| 	/// declaration.
 | |
| 	/// @return A vector of strings containing the variable names used in
 | |
| 	/// the declaration statement.
 | |
| 	std::vector<std::string> createVarDecls(unsigned _start, unsigned _end, bool _isAssignment);
 | |
| 
 | |
| 	/// Prints comma separated variable names to output stream and
 | |
| 	/// returns a vector containing the printed variable names.
 | |
| 	/// Example: createVars(0, 2) returns {"x_0", "x_1"} and prints
 | |
| 	///		x_0, x_1
 | |
| 	/// @param _startIdx Start index of variable (inclusive)
 | |
| 	/// @param _endIdx End index of variable (exclusive)
 | |
| 	/// @return A vector of strings containing the printed variable names.
 | |
| 	std::vector<std::string> createVars(unsigned _startIdx, unsigned _endIdx);
 | |
| 
 | |
| 	/// Manages scope of Yul variables
 | |
| 	/// @param _varNames is a list of Yul variable names whose scope needs
 | |
| 	/// to be tracked according to Yul scoping rules.
 | |
| 	void scopeVariables(std::vector<std::string> const& _varNames);
 | |
| 
 | |
| 	/// Print the Yul syntax to make a call to a function named @a _funcName to
 | |
| 	/// the output stream.
 | |
| 	/// @param _funcName Name of the function to be called
 | |
| 	/// @param _numInParams Number of input parameters in function signature
 | |
| 	/// @param _numOutParams Number of output parameters in function signature
 | |
| 	void createFunctionCall(std::string _funcName, unsigned _numInParams, unsigned _numOutParams);
 | |
| 
 | |
| 	/// Print the Yul syntax to pass input arguments to a function that has
 | |
| 	/// @a _numInParams number of input parameters to the output stream.
 | |
| 	/// The input arguments are pseudo-randomly chosen from calldata, memory,
 | |
| 	/// storage, or the Yul optimizer hex dictionary.
 | |
| 	/// @param _numInParams Number of input arguments to fill
 | |
| 	void fillFunctionCallInput(unsigned _numInParams);
 | |
| 
 | |
| 	/// Print the Yul syntax to save values returned by a function call
 | |
| 	/// to the output stream. The values are either stored to memory or
 | |
| 	/// storage based on a simulated coin flip. The saved location is
 | |
| 	/// decided pseudo-randomly.
 | |
| 	/// @param _varsVec A vector of strings that reference variables
 | |
| 	/// holding the return values of a function call.
 | |
| 	void saveFunctionCallOutput(std::vector<std::string> const& _varsVec);
 | |
| 
 | |
| 	/// Register a function declaration
 | |
| 	/// @param _f Pointer to a FunctionDef object
 | |
| 	void registerFunction(FunctionDef const* _f);
 | |
| 
 | |
| 	/// Removes entry from m_functionMap and m_functionName
 | |
| 	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.
 | |
| 	/// @param _p Enum that decides if the returned token is hex prefixed ("0x") or not
 | |
| 	/// @return Dictionary token at the index computed using a
 | |
| 	/// monotonically increasing counter as follows:
 | |
| 	///		index = (m_inputSize * m_inputSize + counter) % dictionarySize
 | |
| 	/// where m_inputSize is the size of the protobuf input and
 | |
| 	/// dictionarySize is the total number of entries in the dictionary.
 | |
| 	std::string dictionaryToken(util::HexPrefix _p = util::HexPrefix::Add);
 | |
| 
 | |
| 	/// Returns an EVMVersion object corresponding to the protobuf
 | |
| 	/// enum of type Program_Version
 | |
| 	solidity::langutil::EVMVersion evmVersionMapping(Program_Version const& _x);
 | |
| 
 | |
| 	/// Returns a monotonically increasing counter that starts from zero.
 | |
| 	unsigned counter()
 | |
| 	{
 | |
| 		return m_counter++;
 | |
| 	}
 | |
| 
 | |
| 	/// Generate function name of the form "foo_<typeSuffix>_<counter>".
 | |
| 	/// @param _type Type classified according to the number of
 | |
| 	/// values returned by function.
 | |
| 	std::string functionName(NumFunctionReturns _type)
 | |
| 	{
 | |
| 		return "foo_" + functionTypeToString(_type) + "_" + std::to_string(counter());
 | |
| 	}
 | |
| 
 | |
| 	/// Returns a pseudo-randomly chosen object identifier that is in the
 | |
| 	/// 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;
 | |
| 	/// Variables in all function definitions
 | |
| 	std::vector<std::vector<std::vector<std::string>>> m_funcVars;
 | |
| 	/// Variables in current function definition
 | |
| 	std::vector<std::string const*> m_currentFuncVars;
 | |
| 	/// Variables in global scope
 | |
| 	std::vector<std::string const*> m_currentGlobalVars;
 | |
| 	/// Functions in current scope
 | |
| 	std::vector<std::vector<std::string>> m_scopeFuncs;
 | |
| 	/// Global variables
 | |
| 	std::vector<std::vector<std::string>> m_globalVars;
 | |
| 	/// Variables declared in for loop init block that is in global scope
 | |
| 	std::vector<std::vector<std::string>> m_globalForLoopInitVars;
 | |
| 	/// Variables declared in for loop init block that is in function scope
 | |
| 	std::vector<std::vector<std::vector<std::string>>> m_funcForLoopInitVars;
 | |
| 	/// Vector of function names
 | |
| 	std::vector<std::string> m_functions;
 | |
| 	/// Maps FunctionDef object to its name
 | |
| 	std::map<FunctionDef const*, std::string> m_functionDefMap;
 | |
| 	// Set that is used for deduplicating switch case literals
 | |
| 	std::stack<std::set<u256>> m_switchLiteralSetPerScope;
 | |
| 	// 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;
 | |
| 	/// Map of object name to list of sub-object namespace(s) in scope
 | |
| 	std::map<std::string, std::vector<std::string>> m_objectScope;
 | |
| 	// 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_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
 | |
| 	/// statements can not be created.
 | |
| 	bool m_inForBodyScope;
 | |
| 	/// Maximum number of for loops that a test case may contain
 | |
| 	static auto constexpr s_maxForLoops = 2;
 | |
| 	// Index used for naming loop variable of bounded for loops
 | |
| 	unsigned m_numNestedForLoops;
 | |
| 	/// Counter for number of for loops
 | |
| 	unsigned m_numForLoops;
 | |
| 	/// Predicate to keep track of for loop init scope. If true, variable
 | |
| 	/// or function declarations can not be created.
 | |
| 	bool m_inForInitScope;
 | |
| 	/// Flag that is true while converting for loop condition,
 | |
| 	/// false otherwise.
 | |
| 	bool m_inForCond;
 | |
| 	/// Monotonically increasing counter
 | |
| 	unsigned m_counter;
 | |
| 	/// Size of protobuf input
 | |
| 	unsigned m_inputSize;
 | |
| 	/// Predicate that is true if inside function definition, false otherwise
 | |
| 	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
 | |
| 	/// block is enabled.
 | |
| 	bool m_forInitScopeExtEnabled;
 | |
| 	/// Object that holds the targeted evm version specified by protobuf input
 | |
| 	solidity::langutil::EVMVersion m_evmVersion;
 | |
| };
 | |
| }
 |