yul proto fuzzer: Generalize variable references

This commit is contained in:
Bhargava Shastry 2019-11-05 23:37:04 +01:00
parent 95eafba7d9
commit 41bdc9b673
3 changed files with 113 additions and 53 deletions

View File

@ -90,12 +90,33 @@ string ProtoConverter::visit(Literal const& _x)
} }
} }
void ProtoConverter::consolidateVarDeclsInFunctionDef()
{
m_currentFuncVars.clear();
auto &scopes = m_funcVars.back();
for (auto &s: scopes)
m_currentFuncVars.insert(m_currentFuncVars.end(), s.begin(), s.end());
}
void ProtoConverter::consolidateGlobalVarDecls()
{
m_globalVars.clear();
for (auto &scope: m_variables)
m_globalVars.insert(m_globalVars.end(), scope.begin(), scope.end());
}
bool ProtoConverter::varDeclAvailable() bool ProtoConverter::varDeclAvailable()
{ {
if (m_inFunctionDef) if (m_inFunctionDef)
return m_scopeVars.top().size() > 0; {
consolidateVarDeclsInFunctionDef();
return m_currentFuncVars.size() > 0;
}
else else
return m_variables.size() > 0; {
consolidateGlobalVarDecls();
return m_globalVars.size() > 0;
}
} }
bool ProtoConverter::functionCallNotPossible(FunctionCall_Returns _type) bool ProtoConverter::functionCallNotPossible(FunctionCall_Returns _type)
@ -109,14 +130,14 @@ void ProtoConverter::visit(VarRef const& _x)
if (m_inFunctionDef) if (m_inFunctionDef)
{ {
// Ensure that there is at least one variable declaration to reference in function scope. // Ensure that there is at least one variable declaration to reference in function scope.
yulAssert(m_scopeVars.top().size() > 0, "Proto fuzzer: No variables to reference."); yulAssert(m_currentFuncVars.size() > 0, "Proto fuzzer: No variables to reference.");
m_output << m_scopeVars.top()[_x.varnum() % m_scopeVars.top().size()]; m_output << m_currentFuncVars[_x.varnum() % m_currentFuncVars.size()];
} }
else else
{ {
// Ensure that there is at least one variable declaration to reference in nested scopes. // Ensure that there is at least one variable declaration to reference in nested scopes.
yulAssert(m_variables.size() > 0, "Proto fuzzer: No variables to reference."); yulAssert(m_globalVars.size() > 0, "Proto fuzzer: No global variables to reference.");
m_output << m_variables[_x.varnum() % m_variables.size()]; m_output << m_globalVars[_x.varnum() % m_globalVars.size()];
} }
} }
@ -258,8 +279,10 @@ void ProtoConverter::visit(VarDecl const& _x)
m_output << "let " << varName << " := "; m_output << "let " << varName << " := ";
visit(_x.expr()); visit(_x.expr());
m_output << "\n"; m_output << "\n";
m_scopeVars.top().push_back(varName); if (m_inFunctionDef)
m_variables.push_back(varName); m_funcVars.back().back().push_back(varName);
else
m_variables.back().push_back(varName);
} }
void ProtoConverter::visit(TypedVarDecl const& _x) void ProtoConverter::visit(TypedVarDecl const& _x)
@ -324,8 +347,10 @@ void ProtoConverter::visit(TypedVarDecl const& _x)
m_output << " : u256\n"; m_output << " : u256\n";
break; break;
} }
m_scopeVars.top().push_back(varName); if (m_inFunctionDef)
m_variables.push_back(varName); m_funcVars.back().back().push_back(varName);
else
m_variables.back().push_back(varName);
} }
void ProtoConverter::visit(UnaryOp const& _x) void ProtoConverter::visit(UnaryOp const& _x)
@ -1102,12 +1127,19 @@ void ProtoConverter::visit(Statement const& _x)
} }
} }
void ProtoConverter::openScope(vector<string> const& _funcParams) void ProtoConverter::openBlockScope()
{ {
m_scopeVars.push({}); m_scopeFuncs.push_back({});
m_scopeFuncs.push({}); // Create new block scope inside current function scope
if (!_funcParams.empty()) if (m_inFunctionDef)
addVarsToScope(_funcParams); m_funcVars.back().push_back(vector<string>{});
else
m_variables.push_back(vector<string>{});
}
void ProtoConverter::openFunctionScope(vector<string> const& _funcParams)
{
m_funcVars.push_back(vector<vector<string>>({_funcParams}));
} }
void ProtoConverter::updateFunctionMaps(string const& _var) void ProtoConverter::updateFunctionMaps(string const& _var)
@ -1124,21 +1156,9 @@ void ProtoConverter::updateFunctionMaps(string const& _var)
yulAssert(erased == 2, "Proto fuzzer: Function maps not updated"); yulAssert(erased == 2, "Proto fuzzer: Function maps not updated");
} }
void ProtoConverter::closeScope() void ProtoConverter::closeBlockScope()
{ {
for (auto const& var: m_scopeVars.top()) for (auto const& f: m_scopeFuncs.back())
{
unsigned numVarsRemoved = m_variables.size();
m_variables.erase(remove(m_variables.begin(), m_variables.end(), var), m_variables.end());
numVarsRemoved -= m_variables.size();
yulAssert(
numVarsRemoved == 1,
"Proto fuzzer: Nothing or too much went out of scope"
);
}
m_scopeVars.pop();
for (auto const& f: m_scopeFuncs.top())
{ {
unsigned numFuncsRemoved = m_functions.size(); unsigned numFuncsRemoved = m_functions.size();
m_functions.erase(remove(m_functions.begin(), m_functions.end(), f), m_functions.end()); m_functions.erase(remove(m_functions.begin(), m_functions.end(), f), m_functions.end());
@ -1149,21 +1169,40 @@ void ProtoConverter::closeScope()
); );
updateFunctionMaps(f); updateFunctionMaps(f);
} }
m_scopeFuncs.pop(); if (!m_scopeFuncs.empty())
m_scopeFuncs.pop_back();
if (!m_inFunctionDef)
{
if (!m_variables.empty())
m_variables.pop_back();
}
else
{
// Variables that have been declared in a
// function block, go out of scope
if (!m_funcVars.empty())
if (!m_funcVars.back().empty())
m_funcVars.back().pop_back();
}
}
void ProtoConverter::closeFunctionScope()
{
if (!m_funcVars.empty())
m_funcVars.pop_back();
} }
void ProtoConverter::addVarsToScope(vector<string> const& _vars) void ProtoConverter::addVarsToScope(vector<string> const& _vars)
{ {
for (string const& i: _vars) if (m_inFunctionDef)
{ m_funcVars.back().back().insert(m_funcVars.back().back().end(), _vars.begin(), _vars.end());
m_variables.push_back(i); else
m_scopeVars.top().push_back(i); m_variables.back().insert(m_variables.back().end(), _vars.begin(), _vars.end());
}
} }
void ProtoConverter::visit(Block const& _x, vector<string> _funcParams) void ProtoConverter::visit(Block const& _x)
{ {
openScope(_funcParams); openBlockScope();
// Register function declarations in this scope unless this // Register function declarations in this scope unless this
// scope belongs to for-init (in which function declarations // scope belongs to for-init (in which function declarations
@ -1181,7 +1220,7 @@ void ProtoConverter::visit(Block const& _x, vector<string> _funcParams)
} }
else else
m_output << "{}\n"; m_output << "{}\n";
closeScope(); closeBlockScope();
} }
vector<string> ProtoConverter::createVars(unsigned _startIdx, unsigned _endIdx) vector<string> ProtoConverter::createVars(unsigned _startIdx, unsigned _endIdx)
@ -1224,7 +1263,7 @@ void ProtoConverter::registerFunction(FunctionDef const* _x)
auto ret = m_functionSigMap.emplace(make_pair(funcName, make_pair(numInParams, numOutParams))); auto ret = m_functionSigMap.emplace(make_pair(funcName, make_pair(numInParams, numOutParams)));
yulAssert(ret.second, "Proto fuzzer: Function already exists."); yulAssert(ret.second, "Proto fuzzer: Function already exists.");
m_functions.push_back(funcName); m_functions.push_back(funcName);
m_scopeFuncs.top().push_back(funcName); m_scopeFuncs.back().push_back(funcName);
m_functionDefMap.emplace(make_pair(_x, funcName)); m_functionDefMap.emplace(make_pair(_x, funcName));
} }
@ -1363,8 +1402,12 @@ void ProtoConverter::createFunctionDefAndCall(
bool wasInFunctionDef = m_inFunctionDef; bool wasInFunctionDef = m_inFunctionDef;
m_inFunctionDef = true; m_inFunctionDef = true;
// Body // Create new function scope and add function input and return
visit(_x.block(), varsVec); // parameters to it.
openFunctionScope(varsVec);
// Visit function body
visit(_x.block());
closeFunctionScope();
m_inForBodyScope = wasInForBody; m_inForBodyScope = wasInForBody;
m_inFunctionDef = wasInFunctionDef; m_inFunctionDef = wasInFunctionDef;

View File

@ -42,6 +42,8 @@ class ProtoConverter
public: public:
ProtoConverter() ProtoConverter()
{ {
m_funcVars = std::vector<std::vector<std::vector<std::string>>>{};
m_variables = std::vector<std::vector<std::string>>{};
m_inForBodyScope = false; m_inForBodyScope = false;
m_inForInitScope = false; m_inForInitScope = false;
m_numNestedForLoops = 0; m_numNestedForLoops = 0;
@ -62,7 +64,7 @@ private:
/// @param _block Reference to a basic block of yul statements. /// @param _block Reference to a basic block of yul statements.
/// @param _funcParams List of function parameter names, defaults to /// @param _funcParams List of function parameter names, defaults to
/// an empty vector. /// an empty vector.
void visit(Block const& _block, std::vector<std::string> _funcParams = {}); void visit(Block const& _block);
std::string visit(Literal const&); std::string visit(Literal const&);
void visit(VarRef const&); void visit(VarRef const&);
@ -98,11 +100,15 @@ private:
void visit(Code const&); void visit(Code const&);
void visit(Program const&); void visit(Program const&);
/// Creates a new scope, and adds @a _funcParams to it if it /// Creates a new block scope.
void openBlockScope();
/// Creates a new function scope, and adds @a _funcParams to it if it
/// is non-empty. /// is non-empty.
void openScope(std::vector<std::string> const& _funcParams); void openFunctionScope(std::vector<std::string> const& _funcParams);
/// Closes current scope /// Closes current block scope
void closeScope(); void closeBlockScope();
/// Closes current function scope
void closeFunctionScope();
/// Adds @a _vars to current scope /// Adds @a _vars to current scope
void addVarsToScope(std::vector<std::string> const& _vars); void addVarsToScope(std::vector<std::string> const& _vars);
@ -138,6 +144,14 @@ private:
/// Multiple -> "m" /// Multiple -> "m"
std::string functionTypeToString(NumFunctionReturns _type); 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, /// Return true if at least one variable declaration is in scope,
/// false otherwise. /// false otherwise.
/// @return True in the following cases: /// @return True in the following cases:
@ -295,12 +309,16 @@ private:
} }
std::ostringstream m_output; std::ostringstream m_output;
/// Variables in current scope /// Variables in all function definitions
std::stack<std::vector<std::string>> m_scopeVars; std::vector<std::vector<std::vector<std::string>>> m_funcVars;
/// Variables in current function definition
std::vector<std::string> m_currentFuncVars;
/// Variables in global scope
std::vector<std::string> m_globalVars;
/// Functions in current scope /// Functions in current scope
std::stack<std::vector<std::string>> m_scopeFuncs; std::vector<std::vector<std::string>> m_scopeFuncs;
/// Variables /// Variables
std::vector<std::string> m_variables; std::vector<std::vector<std::string>> m_variables;
/// Functions /// Functions
std::vector<std::string> m_functions; std::vector<std::string> m_functions;
/// Maps FunctionDef object to its name /// Maps FunctionDef object to its name

View File

@ -55,8 +55,7 @@ void printErrors(ostream& _stream, ErrorList const& _errors)
DEFINE_PROTO_FUZZER(Program const& _input) DEFINE_PROTO_FUZZER(Program const& _input)
{ {
ProtoConverter converter; string yul_source = ProtoConverter().programToString(_input);
string yul_source = converter.programToString(_input);
if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH"))
{ {
@ -82,7 +81,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
!stack.parserResult()->analysisInfo) !stack.parserResult()->analysisInfo)
{ {
printErrors(std::cout, stack.errors()); printErrors(std::cout, stack.errors());
return; yulAssert(false, "Proto fuzzer generated malformed program");
} }
} }
catch (Exception const&) catch (Exception const&)