[Proto fuzzer] Add function calls, and multi variable declaration/assignment statements

This commit is contained in:
Bhargava Shastry 2019-04-09 08:45:36 +02:00
parent c3a1c168d0
commit 495f7f9013
6 changed files with 849 additions and 410 deletions

View File

@ -33,7 +33,7 @@ target_link_libraries(strictasm_assembly_ossfuzz PRIVATE yul FuzzingEngine.a)
add_executable(yul_proto_ossfuzz yulProtoFuzzer.cpp protoToYul.cpp yulProto.pb.cc)
target_include_directories(yul_proto_ossfuzz PRIVATE /src/libprotobuf-mutator /src/LPM/external.protobuf/include)
target_link_libraries(yul_proto_ossfuzz PRIVATE yul evmasm
target_link_libraries(yul_proto_ossfuzz PRIVATE yul evmasm solidity
protobuf-mutator-libfuzzer.a
protobuf-mutator.a
protobuf.a

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,8 @@
#include <sstream>
#include <stack>
#include <set>
#include <vector>
#include <tuple>
#include <test/tools/ossfuzz/yulProto.pb.h>
#include <libdevcore/Common.h>
@ -38,31 +40,34 @@ class ProtoConverter
public:
ProtoConverter()
{
// The hard-coded function template foo has 10 parameters that are already "live"
m_numLiveVars = 10;
m_numLiveVars = 0;
m_numVarsPerScope.push(m_numLiveVars);
m_numNestedForLoops = 0;
m_inForScope.push(false);
m_numFunctionSets = 0;
}
ProtoConverter(ProtoConverter const&) = delete;
ProtoConverter(ProtoConverter&&) = delete;
std::string functionToString(Function const& _input);
std::string protoToYul(uint8_t const* _data, size_t _size);
std::string programToString(Program const& _input);
private:
void visit(BinaryOp const&);
void visit(Block const&);
void visit(SpecialBlock const&);
void visit(Literal const&);
void visit(VarRef const&);
void visit(Expression const&);
void visit(VarDecl const&);
void visit(EmptyVarDecl const&);
void visit(MultiVarDecl const&);
void visit(TypedVarDecl const&);
void visit(UnaryOp const&);
void visit(AssignmentStatement const&);
void visit(MultiAssignment const&);
void visit(IfStmt const&);
void visit(StoreFunc const&);
void visit(Statement const&);
void visit(Function const&);
void visit(FunctionDefinition const&);
void visit(ForStmt const&);
void visit(CaseStmt const&);
void visit(SwitchStmt const&);
@ -75,19 +80,72 @@ private:
void visit(RetRevStmt const&);
void visit(SelfDestructStmt const&);
void visit(TerminatingStmt const&);
void visit(FunctionCallNoReturnVal const&);
void visit(FunctionCallSingleReturnVal const&);
void visit(FunctionCall const&);
void visit(FunctionDefinitionNoReturnVal const&);
void visit(FunctionDefinitionSingleReturnVal const&);
void visit(FunctionDefinitionMultiReturnVal const&);
void visit(Program const&);
template <class T>
void visit(google::protobuf::RepeatedPtrField<T> const& _repeated_field);
void registerFunction(FunctionDefinition const&);
std::string createHex(std::string const& _hexBytes) const;
std::string createAlphaNum(std::string const& _strBytes) const;
bool isCaseLiteralUnique(Literal const&);
enum class NumFunctionReturns
{
None,
Single,
Multiple
};
template<class T>
void visitFunctionInputParams(T const&, unsigned);
template<class T>
void createFunctionDefAndCall(T const&, unsigned, unsigned, NumFunctionReturns);
std::string functionTypeToString(NumFunctionReturns _type);
template <class T>
void registerFunction(T const& _x, NumFunctionReturns _type, unsigned _numOutputParams = 0)
{
unsigned numInputParams = _x.num_input_params() % modInputParams;
switch (_type)
{
case NumFunctionReturns::None:
m_functionVecNoReturnValue.push_back(numInputParams);
break;
case NumFunctionReturns::Single:
m_functionVecSingleReturnValue.push_back(numInputParams);
break;
case NumFunctionReturns::Multiple:
m_functionVecMultiReturnValue.push_back(std::make_pair(numInputParams, _numOutputParams));
break;
}
}
std::ostringstream m_output;
std::stack<uint8_t> m_numVarsPerScope;
int32_t m_numLiveVars;
int32_t m_numNestedForLoops;
// Number of live variables in inner scope of a function
std::stack<unsigned> m_numVarsPerScope;
// Number of live variables in function scope
unsigned m_numLiveVars;
// Number of nested for loops for loop index referencing
unsigned m_numNestedForLoops;
std::stack<bool> m_inForScope;
// Set that is used for deduplicating switch case literals
std::stack<std::set<dev::u256>> m_switchLiteralSetPerScope;
// Total number of function sets. A function set contains one function of each type defined by
// NumFunctionReturns
unsigned m_numFunctionSets;
// Look-up table per function type that holds the number of input (output) function parameters
std::vector<unsigned> m_functionVecNoReturnValue;
std::vector<unsigned> m_functionVecSingleReturnValue;
std::vector<std::pair<unsigned, unsigned>> m_functionVecMultiReturnValue;
// mod input/output parameters impose an upper bound on the number of input/output parameters a function may have.
static unsigned constexpr modInputParams = 5;
static unsigned constexpr modOutputParams = 5;
};
}
}

View File

@ -21,6 +21,56 @@ message VarDecl {
required Expression expr = 1;
}
message FunctionCallNoReturnVal {
// Indexes a function that does not return anything
required uint32 func_index = 1;
required Expression in_param1 = 2;
required Expression in_param2 = 3;
required Expression in_param3 = 4;
required Expression in_param4 = 5;
}
// Used by Expression
message FunctionCallSingleReturnVal {
// Indexes a function that returns exactly one value
required uint32 func_index = 1;
required Expression in_param1 = 2;
required Expression in_param2 = 3;
required Expression in_param3 = 4;
required Expression in_param4 = 5;
}
message MultiVarDecl {
// Indexes a function that returns more than one value
required uint32 func_index = 1;
required Expression in_param1 = 2;
required Expression in_param2 = 3;
required Expression in_param3 = 4;
required Expression in_param4 = 5;
}
message MultiAssignment {
// Indexes a function that returns more than one value
required uint32 func_index = 1;
required Expression in_param1 = 2;
required Expression in_param2 = 3;
required Expression in_param3 = 4;
required Expression in_param4 = 5;
required VarRef out_param1 = 6;
required VarRef out_param2 = 7;
required VarRef out_param3 = 8;
required VarRef out_param4 = 9;
}
// We exclude function calls with single return value here and use them as expressions
message FunctionCall {
oneof functioncall_oneof {
FunctionCallNoReturnVal call_zero = 1;
MultiVarDecl call_multidecl = 2;
MultiAssignment call_multiassign = 3;
}
}
message TypedVarDecl {
enum TypeName {
BOOL = 1;
@ -192,6 +242,7 @@ message Expression {
UnaryOp unop = 4;
TernaryOp top = 5;
NullaryOp nop = 6;
FunctionCallSingleReturnVal func_expr = 7;
}
}
@ -253,21 +304,26 @@ message TerminatingStmt {
}
}
// Stub for a VarDecl without an Expression on the RHS
message EmptyVarDecl {}
// TODO: Make Function definition a Statement
message Statement {
oneof stmt_oneof {
VarDecl decl = 1;
AssignmentStatement assignment = 2;
IfStmt ifstmt = 3;
StoreFunc storage_func = 4;
Block blockstmt = 5;
ForStmt forstmt = 6;
SwitchStmt switchstmt = 7;
BreakStmt breakstmt = 8;
ContinueStmt contstmt = 9;
LogFunc log_func = 10;
CopyFunc copy_func = 11;
ExtCodeCopy extcode_copy = 12;
TerminatingStmt terminatestmt = 13;
VarDecl decl = 1;
AssignmentStatement assignment = 2;
IfStmt ifstmt = 3;
StoreFunc storage_func = 4;
Block blockstmt = 5;
ForStmt forstmt = 6;
SwitchStmt switchstmt = 7;
BreakStmt breakstmt = 8;
ContinueStmt contstmt = 9;
LogFunc log_func = 10;
CopyFunc copy_func = 11;
ExtCodeCopy extcode_copy = 12;
TerminatingStmt terminatestmt = 13;
FunctionCall functioncall = 14;
}
}
@ -275,8 +331,39 @@ message Block {
repeated Statement statements = 1;
}
message Function {
required Block statements = 1;
// Identical to Block with the addition of an empty var right at the top
// Used by FunctionDefinitionNoReturnVal only.
message SpecialBlock {
required EmptyVarDecl var = 1;
repeated Statement statements = 2;
}
// This ensures that proto mutator generates at least one of each type if it creates at least 1 functiondef message.
message FunctionDefinition {
required FunctionDefinitionNoReturnVal fd_zero = 1;
required FunctionDefinitionSingleReturnVal fd_one = 2;
required FunctionDefinitionMultiReturnVal fd_multi = 3;
}
// Since this function can have 0 parameters, we hoist an empty var decl at the top via SpecialBlock.
message FunctionDefinitionNoReturnVal {
required uint32 num_input_params = 1;
required SpecialBlock statements = 2;
}
message FunctionDefinitionSingleReturnVal {
required uint32 num_input_params = 1;
required Block statements = 2;
}
message FunctionDefinitionMultiReturnVal {
required uint32 num_input_params = 1;
required uint32 num_output_params = 2;
required Block statements = 3;
}
message Program {
repeated FunctionDefinition funcs = 1;
}
package yul.test.yul_fuzzer;

View File

@ -30,12 +30,10 @@ using namespace yul;
using namespace yul::test::yul_fuzzer;
using namespace std;
DEFINE_PROTO_FUZZER(Function const& _input)
DEFINE_PROTO_FUZZER(Program const& _input)
{
ProtoConverter converter;
string yul_source = converter.functionToString(_input);
if (yul_source.size() > 600)
return;
string yul_source = converter.programToString(_input);
if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH"))
{
@ -45,6 +43,9 @@ DEFINE_PROTO_FUZZER(Function const& _input)
of.write(yul_source.data(), yul_source.size());
}
if (yul_source.size() > 1200)
return;
// AssemblyStack entry point
AssemblyStack stack(
langutil::EVMVersion(),

View File

@ -37,12 +37,10 @@ using namespace langutil;
using namespace dev;
using namespace yul::test;
DEFINE_PROTO_FUZZER(Function const& _input)
DEFINE_PROTO_FUZZER(Program const& _input)
{
ProtoConverter converter;
string yul_source = converter.functionToString(_input);
if (yul_source.size() > 600)
return;
string yul_source = converter.programToString(_input);
if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH"))
{
@ -52,6 +50,9 @@ DEFINE_PROTO_FUZZER(Function const& _input)
of.write(yul_source.data(), yul_source.size());
}
if (yul_source.size() > 1200)
return;
// AssemblyStack entry point
AssemblyStack stack(
langutil::EVMVersion(),