mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add multi variable decl statement; use dummy expressions instead of dictionary tokens alone; dont visit blocks unless they contain at least one statement
This commit is contained in:
parent
31a5d3077e
commit
8aa7abaeda
@ -34,6 +34,41 @@ using namespace solidity::langutil;
|
|||||||
using namespace solidity::util;
|
using namespace solidity::util;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
|
|
||||||
|
string ProtoConverter::dummyExpression()
|
||||||
|
{
|
||||||
|
string expression{};
|
||||||
|
string location{};
|
||||||
|
unsigned pseudoRandomNum = m_inputSize / 13;
|
||||||
|
if (varDeclAvailable())
|
||||||
|
location = varRef(pseudoRandomNum);
|
||||||
|
switch (pseudoRandomNum % 4)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
if (location.empty())
|
||||||
|
expression = "mload(0)";
|
||||||
|
else
|
||||||
|
expression = Whiskers(R"(mload(<loc>))")("loc", location).render();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (location.empty())
|
||||||
|
expression = "sload(0)";
|
||||||
|
else
|
||||||
|
expression = Whiskers(R"(sload(<loc>))")("loc", location).render();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (location.empty())
|
||||||
|
expression = "calldataload(0)";
|
||||||
|
else
|
||||||
|
expression = Whiskers(R"(calldataload(<loc>))")("loc", location).render();
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
expression = dictionaryToken();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
yulAssert(!expression.empty(), "Proto fuzzer: Invalid dummy expression");
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
string ProtoConverter::dictionaryToken(HexPrefix _p)
|
string ProtoConverter::dictionaryToken(HexPrefix _p)
|
||||||
{
|
{
|
||||||
std::string token;
|
std::string token;
|
||||||
@ -176,22 +211,27 @@ bool ProtoConverter::functionCallNotPossible(FunctionCall_Returns _type)
|
|||||||
(_type == FunctionCall::MULTIASSIGN && !varDeclAvailable());
|
(_type == FunctionCall::MULTIASSIGN && !varDeclAvailable());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtoConverter::visit(VarRef const& _x)
|
string ProtoConverter::varRef(unsigned _index)
|
||||||
{
|
{
|
||||||
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_currentFuncVars.size() > 0, "Proto fuzzer: No variables to reference.");
|
yulAssert(m_currentFuncVars.size() > 0, "Proto fuzzer: No variables to reference.");
|
||||||
m_output << *m_currentFuncVars[_x.varnum() % m_currentFuncVars.size()];
|
return *m_currentFuncVars[_index % 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_currentGlobalVars.size() > 0, "Proto fuzzer: No global variables to reference.");
|
yulAssert(m_currentGlobalVars.size() > 0, "Proto fuzzer: No global variables to reference.");
|
||||||
m_output << *m_currentGlobalVars[_x.varnum() % m_currentGlobalVars.size()];
|
return *m_currentGlobalVars[_index % m_currentGlobalVars.size()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProtoConverter::visit(VarRef const& _x)
|
||||||
|
{
|
||||||
|
m_output << varRef(_x.varnum());
|
||||||
|
}
|
||||||
|
|
||||||
void ProtoConverter::visit(Expression const& _x)
|
void ProtoConverter::visit(Expression const& _x)
|
||||||
{
|
{
|
||||||
switch (_x.expr_oneof_case())
|
switch (_x.expr_oneof_case())
|
||||||
@ -201,7 +241,7 @@ void ProtoConverter::visit(Expression const& _x)
|
|||||||
// (because there are no variables in scope), we silently output a literal
|
// (because there are no variables in scope), we silently output a literal
|
||||||
// expression from the optimizer dictionary.
|
// expression from the optimizer dictionary.
|
||||||
if (!varDeclAvailable())
|
if (!varDeclAvailable())
|
||||||
m_output << dictionaryToken();
|
m_output << dummyExpression();
|
||||||
else
|
else
|
||||||
visit(_x.varref());
|
visit(_x.varref());
|
||||||
break;
|
break;
|
||||||
@ -232,7 +272,7 @@ void ProtoConverter::visit(Expression const& _x)
|
|||||||
if (_x.func_expr().ret() == FunctionCall::SINGLE)
|
if (_x.func_expr().ret() == FunctionCall::SINGLE)
|
||||||
visit(_x.func_expr());
|
visit(_x.func_expr());
|
||||||
else
|
else
|
||||||
m_output << dictionaryToken();
|
m_output << dummyExpression();
|
||||||
break;
|
break;
|
||||||
case Expression::kLowcall:
|
case Expression::kLowcall:
|
||||||
visit(_x.lowcall());
|
visit(_x.lowcall());
|
||||||
@ -244,10 +284,10 @@ void ProtoConverter::visit(Expression const& _x)
|
|||||||
if (m_isObject)
|
if (m_isObject)
|
||||||
visit(_x.unopdata());
|
visit(_x.unopdata());
|
||||||
else
|
else
|
||||||
m_output << dictionaryToken();
|
m_output << dummyExpression();
|
||||||
break;
|
break;
|
||||||
case Expression::EXPR_ONEOF_NOT_SET:
|
case Expression::EXPR_ONEOF_NOT_SET:
|
||||||
m_output << dictionaryToken();
|
m_output << dummyExpression();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,7 +299,7 @@ void ProtoConverter::visit(BinaryOp const& _x)
|
|||||||
if ((op == BinaryOp::SHL || op == BinaryOp::SHR || op == BinaryOp::SAR) &&
|
if ((op == BinaryOp::SHL || op == BinaryOp::SHR || op == BinaryOp::SAR) &&
|
||||||
!m_evmVersion.hasBitwiseShifting())
|
!m_evmVersion.hasBitwiseShifting())
|
||||||
{
|
{
|
||||||
m_output << dictionaryToken();
|
m_output << dummyExpression();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,6 +438,81 @@ void ProtoConverter::visit(VarDecl const& _x)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProtoConverter::visit(MultiVarDecl const& _x)
|
||||||
|
{
|
||||||
|
m_output << "let ";
|
||||||
|
vector<string> varNames;
|
||||||
|
// We support up to 4 variables in a single
|
||||||
|
// declaration statement.
|
||||||
|
unsigned numVars = _x.num_vars() % 3 + 2;
|
||||||
|
string delimiter = "";
|
||||||
|
for (unsigned i = 0; i < numVars; i++)
|
||||||
|
{
|
||||||
|
string varName = newVarName();
|
||||||
|
varNames.push_back(varName);
|
||||||
|
m_output << delimiter << varName;
|
||||||
|
if (i == 0)
|
||||||
|
delimiter = ", ";
|
||||||
|
}
|
||||||
|
m_output << "\n";
|
||||||
|
|
||||||
|
// If we are inside a for-init block, there are two places
|
||||||
|
// where the visited vardecl may have been defined:
|
||||||
|
// - directly inside the for-init block
|
||||||
|
// - inside a block within the for-init block
|
||||||
|
// In the latter case, we don't scope extend.
|
||||||
|
if (m_inFunctionDef)
|
||||||
|
{
|
||||||
|
// Variables declared directly in for-init block
|
||||||
|
// are tracked separately because their scope
|
||||||
|
// extends beyond the block they are defined in
|
||||||
|
// to the rest of the for-loop statement.
|
||||||
|
if (m_inForInitScope && m_forInitScopeExtEnabled)
|
||||||
|
{
|
||||||
|
yulAssert(
|
||||||
|
!m_funcForLoopInitVars.empty() && !m_funcForLoopInitVars.back().empty(),
|
||||||
|
"Proto fuzzer: Invalid operation"
|
||||||
|
);
|
||||||
|
for (auto const& varName: varNames)
|
||||||
|
m_funcForLoopInitVars.back().back().push_back(varName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
yulAssert(
|
||||||
|
!m_funcVars.empty() && !m_funcVars.back().empty(),
|
||||||
|
"Proto fuzzer: Invalid operation"
|
||||||
|
);
|
||||||
|
for (auto const& varName: varNames)
|
||||||
|
m_funcVars.back().back().push_back(varName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (m_inForInitScope && m_forInitScopeExtEnabled)
|
||||||
|
{
|
||||||
|
yulAssert(
|
||||||
|
!m_globalForLoopInitVars.empty(),
|
||||||
|
"Proto fuzzer: Invalid operation"
|
||||||
|
);
|
||||||
|
|
||||||
|
for (auto const& varName: varNames)
|
||||||
|
m_globalForLoopInitVars.back().push_back(varName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
yulAssert(
|
||||||
|
!m_globalVars.empty(),
|
||||||
|
"Proto fuzzer: Invalid operation"
|
||||||
|
);
|
||||||
|
|
||||||
|
for (auto const& varName: varNames)
|
||||||
|
m_globalVars.back().push_back(varName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void ProtoConverter::visit(TypedVarDecl const& _x)
|
void ProtoConverter::visit(TypedVarDecl const& _x)
|
||||||
{
|
{
|
||||||
string varName = newVarName();
|
string varName = newVarName();
|
||||||
@ -517,7 +632,7 @@ void ProtoConverter::visit(UnaryOp const& _x)
|
|||||||
// token.
|
// token.
|
||||||
if (op == UnaryOp::EXTCODEHASH && !m_evmVersion.hasExtCodeHash())
|
if (op == UnaryOp::EXTCODEHASH && !m_evmVersion.hasExtCodeHash())
|
||||||
{
|
{
|
||||||
m_output << dictionaryToken();
|
m_output << dummyExpression();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -601,7 +716,7 @@ void ProtoConverter::visit(NullaryOp const& _x)
|
|||||||
if (m_evmVersion.supportsReturndata())
|
if (m_evmVersion.supportsReturndata())
|
||||||
m_output << "returndatasize()";
|
m_output << "returndatasize()";
|
||||||
else
|
else
|
||||||
m_output << dictionaryToken();
|
m_output << dummyExpression();
|
||||||
break;
|
break;
|
||||||
case NullaryOp::ADDRESS:
|
case NullaryOp::ADDRESS:
|
||||||
m_output << "address()";
|
m_output << "address()";
|
||||||
@ -639,7 +754,7 @@ void ProtoConverter::visit(NullaryOp const& _x)
|
|||||||
if (m_evmVersion.hasSelfBalance())
|
if (m_evmVersion.hasSelfBalance())
|
||||||
m_output << "selfbalance()";
|
m_output << "selfbalance()";
|
||||||
else
|
else
|
||||||
m_output << dictionaryToken();
|
m_output << dummyExpression();
|
||||||
break;
|
break;
|
||||||
case NullaryOp::CHAINID:
|
case NullaryOp::CHAINID:
|
||||||
// Replace calls to chainid() on unsupported EVMs with a dictionary
|
// Replace calls to chainid() on unsupported EVMs with a dictionary
|
||||||
@ -647,7 +762,7 @@ void ProtoConverter::visit(NullaryOp const& _x)
|
|||||||
if (m_evmVersion.hasChainID())
|
if (m_evmVersion.hasChainID())
|
||||||
m_output << "chainid()";
|
m_output << "chainid()";
|
||||||
else
|
else
|
||||||
m_output << dictionaryToken();
|
m_output << dummyExpression();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -866,7 +981,7 @@ void ProtoConverter::visit(FunctionCall const& _x)
|
|||||||
// If there are no functions available, calls to functions that
|
// If there are no functions available, calls to functions that
|
||||||
// return a single value may be replaced by a dictionary token.
|
// return a single value may be replaced by a dictionary token.
|
||||||
if (funcType == FunctionCall::SINGLE)
|
if (funcType == FunctionCall::SINGLE)
|
||||||
m_output << dictionaryToken();
|
m_output << dummyExpression();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -878,7 +993,7 @@ void ProtoConverter::visit(FunctionCall const& _x)
|
|||||||
if (!functionValid(funcType, numOutParams))
|
if (!functionValid(funcType, numOutParams))
|
||||||
{
|
{
|
||||||
if (funcType == FunctionCall::SINGLE)
|
if (funcType == FunctionCall::SINGLE)
|
||||||
m_output << dictionaryToken();
|
m_output << dummyExpression();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1010,7 +1125,7 @@ void ProtoConverter::visit(Create const& _x)
|
|||||||
// token.
|
// token.
|
||||||
if (type == Create::CREATE2 && !m_evmVersion.hasCreate2())
|
if (type == Create::CREATE2 && !m_evmVersion.hasCreate2())
|
||||||
{
|
{
|
||||||
m_output << dictionaryToken();
|
m_output << dummyExpression();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1306,19 +1421,23 @@ void ProtoConverter::visit(Statement const& _x)
|
|||||||
visit(_x.assignment());
|
visit(_x.assignment());
|
||||||
break;
|
break;
|
||||||
case Statement::kIfstmt:
|
case Statement::kIfstmt:
|
||||||
visit(_x.ifstmt());
|
if (_x.ifstmt().if_body().statements_size() > 0)
|
||||||
|
visit(_x.ifstmt());
|
||||||
break;
|
break;
|
||||||
case Statement::kStorageFunc:
|
case Statement::kStorageFunc:
|
||||||
visit(_x.storage_func());
|
visit(_x.storage_func());
|
||||||
break;
|
break;
|
||||||
case Statement::kBlockstmt:
|
case Statement::kBlockstmt:
|
||||||
visit(_x.blockstmt());
|
if (_x.blockstmt().statements_size() > 0)
|
||||||
|
visit(_x.blockstmt());
|
||||||
break;
|
break;
|
||||||
case Statement::kForstmt:
|
case Statement::kForstmt:
|
||||||
visit(_x.forstmt());
|
if (_x.forstmt().for_body().statements_size() > 0)
|
||||||
|
visit(_x.forstmt());
|
||||||
break;
|
break;
|
||||||
case Statement::kBoundedforstmt:
|
case Statement::kBoundedforstmt:
|
||||||
visit(_x.boundedforstmt());
|
if (_x.boundedforstmt().for_body().statements_size() > 0)
|
||||||
|
visit(_x.boundedforstmt());
|
||||||
break;
|
break;
|
||||||
case Statement::kSwitchstmt:
|
case Statement::kSwitchstmt:
|
||||||
visit(_x.switchstmt());
|
visit(_x.switchstmt());
|
||||||
@ -1350,8 +1469,9 @@ void ProtoConverter::visit(Statement const& _x)
|
|||||||
visit(_x.functioncall());
|
visit(_x.functioncall());
|
||||||
break;
|
break;
|
||||||
case Statement::kFuncdef:
|
case Statement::kFuncdef:
|
||||||
if (!m_inForInitScope)
|
if (_x.funcdef().block().statements_size() > 0)
|
||||||
visit(_x.funcdef());
|
if (!m_inForInitScope)
|
||||||
|
visit(_x.funcdef());
|
||||||
break;
|
break;
|
||||||
case Statement::kPop:
|
case Statement::kPop:
|
||||||
visit(_x.pop());
|
visit(_x.pop());
|
||||||
@ -1360,6 +1480,9 @@ void ProtoConverter::visit(Statement const& _x)
|
|||||||
if (m_inFunctionDef)
|
if (m_inFunctionDef)
|
||||||
visit(_x.leave());
|
visit(_x.leave());
|
||||||
break;
|
break;
|
||||||
|
case Statement::kMultidecl:
|
||||||
|
visit(_x.multidecl());
|
||||||
|
break;
|
||||||
case Statement::STMT_ONEOF_NOT_SET:
|
case Statement::STMT_ONEOF_NOT_SET:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1528,7 +1651,7 @@ void ProtoConverter::visit(Block const& _x)
|
|||||||
// scope belongs to for-init (in which function declarations
|
// scope belongs to for-init (in which function declarations
|
||||||
// are forbidden).
|
// are forbidden).
|
||||||
for (auto const& statement: _x.statements())
|
for (auto const& statement: _x.statements())
|
||||||
if (statement.has_funcdef() && !m_inForInitScope)
|
if (statement.has_funcdef() && statement.funcdef().block().statements_size() > 0 && !m_inForInitScope)
|
||||||
registerFunction(&statement.funcdef());
|
registerFunction(&statement.funcdef());
|
||||||
|
|
||||||
if (_x.statements_size() > 0)
|
if (_x.statements_size() > 0)
|
||||||
@ -1622,9 +1745,9 @@ void ProtoConverter::fillFunctionCallInput(unsigned _numInParams)
|
|||||||
m_output << "sload(" << slot << ")";
|
m_output << "sload(" << slot << ")";
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
// Call to dictionaryToken() automatically picks a token
|
// Call to dummyExpression() automatically picks a token
|
||||||
// at a pseudo-random location.
|
// at a pseudo-random location.
|
||||||
m_output << dictionaryToken();
|
m_output << dummyExpression();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (i < _numInParams - 1)
|
if (i < _numInParams - 1)
|
||||||
|
@ -78,6 +78,7 @@ private:
|
|||||||
void visit(VarRef const&);
|
void visit(VarRef const&);
|
||||||
void visit(Expression const&);
|
void visit(Expression const&);
|
||||||
void visit(VarDecl const&);
|
void visit(VarDecl const&);
|
||||||
|
void visit(MultiVarDecl const&);
|
||||||
void visit(TypedVarDecl const&);
|
void visit(TypedVarDecl const&);
|
||||||
void visit(UnaryOp const&);
|
void visit(UnaryOp const&);
|
||||||
void visit(AssignmentStatement const&);
|
void visit(AssignmentStatement const&);
|
||||||
@ -283,6 +284,10 @@ private:
|
|||||||
/// enum of type Program_Version
|
/// enum of type Program_Version
|
||||||
solidity::langutil::EVMVersion evmVersionMapping(Program_Version const& _x);
|
solidity::langutil::EVMVersion evmVersionMapping(Program_Version const& _x);
|
||||||
|
|
||||||
|
/// Return variable reference.
|
||||||
|
/// @param _index: Index of variable to be referenced
|
||||||
|
std::string varRef(unsigned _index);
|
||||||
|
|
||||||
/// Returns a monotonically increasing counter that starts from zero.
|
/// Returns a monotonically increasing counter that starts from zero.
|
||||||
unsigned counter()
|
unsigned counter()
|
||||||
{
|
{
|
||||||
@ -321,6 +326,8 @@ private:
|
|||||||
return m_objectId - 1;
|
return m_objectId - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string dummyExpression();
|
||||||
|
|
||||||
std::ostringstream m_output;
|
std::ostringstream m_output;
|
||||||
/// Variables in all function definitions
|
/// Variables in all function definitions
|
||||||
std::vector<std::vector<std::vector<std::string>>> m_funcVars;
|
std::vector<std::vector<std::vector<std::string>>> m_funcVars;
|
||||||
|
@ -21,6 +21,10 @@ message VarDecl {
|
|||||||
required Expression expr = 1;
|
required Expression expr = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message MultiVarDecl {
|
||||||
|
required uint32 num_vars = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message LowLevelCall {
|
message LowLevelCall {
|
||||||
enum Type {
|
enum Type {
|
||||||
CALL = 0;
|
CALL = 0;
|
||||||
@ -384,6 +388,7 @@ message Statement {
|
|||||||
FunctionDef funcdef = 16;
|
FunctionDef funcdef = 16;
|
||||||
PopStmt pop = 17;
|
PopStmt pop = 17;
|
||||||
LeaveStmt leave = 18;
|
LeaveStmt leave = 18;
|
||||||
|
MultiVarDecl multidecl = 19;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user