diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index dcbba388c..e28724098 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -173,20 +173,6 @@ bool ProtoConverter::varDeclAvailable() } } -bool ProtoConverter::functionCallNotPossible(FunctionCall_Returns _type) -{ - return _type == FunctionCall::SINGLE || - (_type == FunctionCall::MULTIASSIGN && !varDeclAvailable()); -} - -unsigned ProtoConverter::numVarsInScope() -{ - if (m_inFunctionDef) - return static_cast(m_currentFuncVars.size()); - else - return static_cast(m_currentGlobalVars.size()); -} - void ProtoConverter::visit(VarRef const& _x) { if (m_inFunctionDef) @@ -238,10 +224,11 @@ void ProtoConverter::visit(Expression const& _x) visit(_x.nop()); break; case Expression::kFuncExpr: - // FunctionCall must return a single value, otherwise - // we output a trivial expression "1". - if (_x.func_expr().ret() == FunctionCall::SINGLE) - visit(_x.func_expr()); + if (auto v = functionExists(NumFunctionReturns::Single); v.has_value()) + { + string functionName = v.value(); + visit(_x.func_expr(), functionName, true); + } else m_output << dictionaryToken(); break; @@ -905,20 +892,6 @@ void ProtoConverter::visitFunctionInputParams(FunctionCall const& _x, unsigned _ } } -bool ProtoConverter::functionValid(FunctionCall_Returns _type, unsigned _numOutParams) -{ - switch (_type) - { - case FunctionCall::ZERO: - return _numOutParams == 0; - case FunctionCall::SINGLE: - return _numOutParams == 1; - case FunctionCall::MULTIDECL: - case FunctionCall::MULTIASSIGN: - return _numOutParams > 1; - } -} - void ProtoConverter::convertFunctionCall( FunctionCall const& _x, string const& _name, @@ -944,130 +917,52 @@ vector ProtoConverter::createVarDecls(unsigned _start, unsigned _end, bo return varsVec; } -void ProtoConverter::visit(FunctionCall const& _x) +optional ProtoConverter::functionExists(NumFunctionReturns _numReturns) { - bool functionAvailable = m_functionSigMap.size() > 0; - unsigned numInParams, numOutParams; - string funcName; - FunctionCall_Returns funcType = _x.ret(); - if (functionAvailable) + for (auto const& item: m_functionSigMap) + if (_numReturns == NumFunctionReturns::None || _numReturns == NumFunctionReturns::Single) + { + if (item.second.second == static_cast(_numReturns)) + return item.first; + } + else + { + if (item.second.second >= static_cast(_numReturns)) + return item.first; + } + return nullopt; +} + +void ProtoConverter::visit(FunctionCall const& _x, string const& _functionName, bool _expression) +{ + yulAssert(m_functionSigMap.count(_functionName), "Proto fuzzer: Invalid function."); + auto ret = m_functionSigMap.at(_functionName); + unsigned numInParams = ret.first; + unsigned numOutParams = ret.second; + + if (numOutParams == 0) { - yulAssert(m_functions.size() > 0, "Proto fuzzer: No function in scope"); - funcName = m_functions[_x.func_index() % m_functions.size()]; - auto ret = m_functionSigMap.at(funcName); - numInParams = ret.first; - numOutParams = ret.second; + convertFunctionCall(_x, _functionName, numInParams); + return; } else { - // If there are no functions available, calls to functions that - // return a single value may be replaced by a dictionary token. - if (funcType == FunctionCall::SINGLE) - m_output << dictionaryToken(); - return; - } - - // If function selected for function call does not meet interface - // requirements (num output values) for the function type - // specified, then we return early unless it is a function call - // that returns a single value (which may be replaced by a - // dictionary token. - if (!functionValid(funcType, numOutParams)) - { - if (funcType == FunctionCall::SINGLE) - m_output << dictionaryToken(); - return; - } - - // If we are here, it means that we have at least one valid - // function for making the function call - switch (funcType) - { - case FunctionCall::ZERO: - convertFunctionCall(_x, funcName, numInParams); - break; - case FunctionCall::SINGLE: - // Since functions that return a single value are used as expressions - // we do not print a newline because it is done by the expression - // visitor. - convertFunctionCall(_x, funcName, numInParams, /*newLine=*/false); - break; - case FunctionCall::MULTIDECL: - { - // Ensure that the chosen function returns at most 4 values - yulAssert( - numOutParams <= 4, - "Proto fuzzer: Function call with too many output params encountered." - ); - - // Obtain variable name suffix - unsigned startIdx = counter(); - vector varsVec = createVarDecls( - startIdx, - startIdx + numOutParams, - /*isAssignment=*/true - ); - - // Create RHS of multi var decl - convertFunctionCall(_x, funcName, numInParams); - // Add newly minted vars in the multidecl statement to current scope - addVarsToScope(varsVec); - break; - } - case FunctionCall::MULTIASSIGN: - // Ensure that the chosen function returns at most 4 values - yulAssert( - numOutParams <= 4, - "Proto fuzzer: Function call with too many output params encountered." - ); - - // Return early if numOutParams > number of available variables - if (numOutParams > numVarsInScope()) - return; - - // Copy variables in scope in order to prevent repeated references - vector variables; - if (m_inFunctionDef) - for (auto var: m_currentFuncVars) - variables.push_back(*var); - else - for (auto var: m_currentGlobalVars) - variables.push_back(*var); - - auto refVar = [](vector& _var, unsigned _rand, bool _comma = true) -> string - { - auto index = _rand % _var.size(); - string ref = _var[index]; - _var.erase(_var.begin() + index); - if (_comma) - ref += ", "; - return ref; - }; - - // Convert LHS of multi assignment - // We reverse the order of out param visits since the order does not matter. - // This helps reduce the size of this switch statement. - switch (numOutParams) + yulAssert(numOutParams > 0, ""); + vector varsVec; + if (!_expression) { - case 4: - m_output << refVar(variables, _x.out_param4().varnum()); - [[fallthrough]]; - case 3: - m_output << refVar(variables, _x.out_param3().varnum()); - [[fallthrough]]; - case 2: - m_output << refVar(variables, _x.out_param2().varnum()); - m_output << refVar(variables, _x.out_param1().varnum(), false); - break; - default: - yulAssert(false, "Proto fuzzer: Function call with too many or too few input parameters."); - break; + // Obtain variable name suffix + unsigned startIdx = counter(); + varsVec = createVarDecls( + startIdx, + startIdx + numOutParams, + /*isAssignment=*/true + ); } - m_output << " := "; - - // Convert RHS of multi assignment - convertFunctionCall(_x, funcName, numInParams); - break; + convertFunctionCall(_x, _functionName, numInParams); + // Add newly minted vars in the multidecl statement to current scope + if (!_expression) + addVarsToScope(varsVec); } } @@ -1463,10 +1358,13 @@ void ProtoConverter::visit(Statement const& _x) visit(_x.terminatestmt()); break; case Statement::kFunctioncall: - // Return early if a function call cannot be created - if (functionCallNotPossible(_x.functioncall().ret())) - return; - visit(_x.functioncall()); + if (!m_functionSigMap.empty()) + { + unsigned index = counter() % m_functionSigMap.size(); + auto iter = m_functionSigMap.begin(); + advance(iter, index); + visit(_x.functioncall(), iter->first); + } break; case Statement::kFuncdef: if (_x.funcdef().block().statements_size() > 0) diff --git a/test/tools/ossfuzz/protoToYul.h b/test/tools/ossfuzz/protoToYul.h index 7e4bb1fff..aff9fe940 100644 --- a/test/tools/ossfuzz/protoToYul.h +++ b/test/tools/ossfuzz/protoToYul.h @@ -102,7 +102,7 @@ private: void visit(RetRevStmt const&); void visit(SelfDestructStmt const&); void visit(TerminatingStmt const&); - void visit(FunctionCall const&); + void visit(FunctionCall const&, std::string const&, bool _expression = false); void visit(FunctionDef const&); void visit(PopStmt const&); void visit(LeaveStmt const&); @@ -125,8 +125,6 @@ private: void closeFunctionScope(); /// Adds @a _vars to current scope void addVarsToScope(std::vector const& _vars); - /// @returns number of variables that are in scope - unsigned numVarsInScope(); std::string createHex(std::string const& _hexBytes); @@ -140,9 +138,9 @@ private: /// alphabets nor digits from it and returns the said string. static std::string createAlphaNum(std::string const& _strBytes); - enum class NumFunctionReturns + enum class NumFunctionReturns: unsigned { - None, + None = 0, Single, Multiple }; @@ -176,34 +174,6 @@ private: /// 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 @@ -295,6 +265,9 @@ private: /// enum of type Program_Version static solidity::langutil::EVMVersion evmVersionMapping(Program_Version const& _x); + /// @returns name of Yul function with return type of @param _numReturns. + std::optional functionExists(NumFunctionReturns _numReturns); + /// Returns a monotonically increasing counter that starts from zero. unsigned counter() { diff --git a/test/tools/ossfuzz/yulProto.proto b/test/tools/ossfuzz/yulProto.proto index 2ea60f8a8..bb2f1170a 100644 --- a/test/tools/ossfuzz/yulProto.proto +++ b/test/tools/ossfuzz/yulProto.proto @@ -58,23 +58,14 @@ message Create { } message FunctionCall { - enum Returns { - ZERO = 1; - SINGLE = 2; - MULTIDECL = 3; - MULTIASSIGN = 4; - } - required Returns ret = 1; - // Indexes an existing function - required uint32 func_index = 2; - required Expression in_param1 = 3; - required Expression in_param2 = 4; - required Expression in_param3 = 5; - required Expression in_param4 = 6; - required VarRef out_param1 = 7; - required VarRef out_param2 = 8; - required VarRef out_param3 = 9; - required VarRef out_param4 = 10; + required Expression in_param1 = 1; + required Expression in_param2 = 2; + required Expression in_param3 = 3; + required Expression in_param4 = 4; + required VarRef out_param1 = 5; + required VarRef out_param2 = 6; + required VarRef out_param3 = 7; + required VarRef out_param4 = 8; } message TypedVarDecl {