Proto yul converter: Provide a flag to filter stateful instructions.

This commit is contained in:
Bhargava Shastry 2021-03-04 15:20:19 +01:00
parent be5647735e
commit a34308e4cb
2 changed files with 69 additions and 6 deletions

View File

@ -249,10 +249,19 @@ void ProtoConverter::visit(Expression const& _x)
visit(_x.lowcall()); visit(_x.lowcall());
break; break;
case Expression::kCreate: case Expression::kCreate:
visit(_x.create()); // Create and create2 return address of created contract which
// may lead to state change via sstore of the returned address.
if (!m_filterStatefulInstructions)
visit(_x.create());
else
m_output << dictionaryToken();
break; break;
case Expression::kUnopdata: case Expression::kUnopdata:
if (m_isObject) // Filter datasize and dataoffset because these instructions may return
// a value that is a function of optimisation. Therefore, when run on
// an EVM client, the execution traces for unoptimised vs optimised
// programs may differ. This ends up as a false-positive bug report.
if (m_isObject && !m_filterStatefulInstructions)
visit(_x.unopdata()); visit(_x.unopdata());
else else
m_output << dictionaryToken(); m_output << dictionaryToken();
@ -572,6 +581,22 @@ void ProtoConverter::visit(UnaryOp const& _x)
return; return;
} }
// The following instructions may lead to change of EVM state and are hence
// excluded to avoid false positives.
if (
m_filterStatefulInstructions &&
(
op == UnaryOp::EXTCODEHASH ||
op == UnaryOp::EXTCODESIZE ||
op == UnaryOp::BALANCE ||
op == UnaryOp::BLOCKHASH
)
)
{
m_output << dictionaryToken();
return;
}
switch (op) switch (op)
{ {
case UnaryOp::NOT: case UnaryOp::NOT:
@ -629,7 +654,26 @@ void ProtoConverter::visit(TernaryOp const& _x)
void ProtoConverter::visit(NullaryOp const& _x) void ProtoConverter::visit(NullaryOp const& _x)
{ {
switch (_x.op()) auto op = _x.op();
// The following instructions may lead to a change in EVM state and are
// excluded to avoid false positive reports.
if (
m_filterStatefulInstructions &&
(
op == NullaryOp::GAS ||
op == NullaryOp::CODESIZE ||
op == NullaryOp::ADDRESS ||
op == NullaryOp::TIMESTAMP ||
op == NullaryOp::NUMBER ||
op == NullaryOp::DIFFICULTY
)
)
{
m_output << dictionaryToken();
return;
}
switch (op)
{ {
case NullaryOp::MSIZE: case NullaryOp::MSIZE:
m_output << "msize()"; m_output << "msize()";
@ -714,6 +758,11 @@ void ProtoConverter::visit(CopyFunc const& _x)
if (type == CopyFunc::RETURNDATA && !m_evmVersion.supportsReturndata()) if (type == CopyFunc::RETURNDATA && !m_evmVersion.supportsReturndata())
return; return;
// Code copy may change state if e.g., some byte of code
// is stored to storage via a sequence of mload and sstore.
if (m_filterStatefulInstructions && type == CopyFunc::CODE)
return;
switch (type) switch (type)
{ {
case CopyFunc::CALLDATA: case CopyFunc::CALLDATA:
@ -1380,7 +1429,7 @@ void ProtoConverter::visit(Statement const& _x)
visit(_x.blockstmt()); visit(_x.blockstmt());
break; break;
case Statement::kForstmt: case Statement::kForstmt:
if (_x.forstmt().for_body().statements_size() > 0) if (_x.forstmt().for_body().statements_size() > 0 && !m_filterUnboundedLoops)
visit(_x.forstmt()); visit(_x.forstmt());
break; break;
case Statement::kBoundedforstmt: case Statement::kBoundedforstmt:
@ -1405,7 +1454,10 @@ void ProtoConverter::visit(Statement const& _x)
visit(_x.copy_func()); visit(_x.copy_func());
break; break;
case Statement::kExtcodeCopy: case Statement::kExtcodeCopy:
visit(_x.extcode_copy()); // Extcodecopy may change state if external code is copied via a
// sequence of mload/sstore.
if (!m_filterStatefulInstructions)
visit(_x.extcode_copy());
break; break;
case Statement::kTerminatestmt: case Statement::kTerminatestmt:
visit(_x.terminatestmt()); visit(_x.terminatestmt());

View File

@ -39,7 +39,10 @@ namespace solidity::yul::test::yul_fuzzer
class ProtoConverter class ProtoConverter
{ {
public: public:
ProtoConverter() ProtoConverter(
bool _filterStatefulInstructions = false,
bool _filterUnboundedLoops = false
)
{ {
m_funcVars = std::vector<std::vector<std::vector<std::string>>>{}; m_funcVars = std::vector<std::vector<std::vector<std::string>>>{};
m_globalVars = std::vector<std::vector<std::string>>{}; m_globalVars = std::vector<std::vector<std::string>>{};
@ -54,6 +57,8 @@ public:
m_objectId = 0; m_objectId = 0;
m_isObject = false; m_isObject = false;
m_forInitScopeExtEnabled = true; m_forInitScopeExtEnabled = true;
m_filterStatefulInstructions = _filterStatefulInstructions;
m_filterUnboundedLoops = _filterUnboundedLoops;
} }
ProtoConverter(ProtoConverter const&) = delete; ProtoConverter(ProtoConverter const&) = delete;
ProtoConverter(ProtoConverter&&) = delete; ProtoConverter(ProtoConverter&&) = delete;
@ -389,5 +394,11 @@ private:
bool m_forInitScopeExtEnabled; bool m_forInitScopeExtEnabled;
/// Object that holds the targeted evm version specified by protobuf input /// Object that holds the targeted evm version specified by protobuf input
solidity::langutil::EVMVersion m_evmVersion; solidity::langutil::EVMVersion m_evmVersion;
/// Flag that, if set, stops the converter from generating state changing
/// opcodes.
bool m_filterStatefulInstructions;
/// Flat that, if set, stops the converter from generating potentially
/// unbounded loops.
bool m_filterUnboundedLoops;
}; };
} }