Add exception for stack too deep error and binop stmt

This commit is contained in:
Bhargava Shastry 2020-05-04 16:42:35 +02:00
parent 9bfab1fe00
commit 3db10b8f69
11 changed files with 110 additions and 41 deletions

View File

@ -38,6 +38,7 @@ class Error;
using ErrorList = std::vector<std::shared_ptr<Error const>>; using ErrorList = std::vector<std::shared_ptr<Error const>>;
struct FuzzerError: virtual util::Exception {}; struct FuzzerError: virtual util::Exception {};
struct StackTooDeepError: virtual util::Exception {};
struct CompilerError: virtual util::Exception {}; struct CompilerError: virtual util::Exception {};
struct InternalCompilerError: virtual util::Exception {}; struct InternalCompilerError: virtual util::Exception {};
struct FatalError: virtual util::Exception {}; struct FatalError: virtual util::Exception {};

View File

@ -219,8 +219,9 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
else else
solUnimplemented("Copying of type " + _sourceType.toString(false) + " to storage not yet supported."); solUnimplemented("Copying of type " + _sourceType.toString(false) + " to storage not yet supported.");
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] <source_value>... // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] <source_value>...
solAssert( assertThrow(
2 + byteOffsetSize + sourceBaseType->sizeOnStack() <= 16, 2 + byteOffsetSize + sourceBaseType->sizeOnStack() <= 16,
StackTooDeepError,
"Stack too deep, try removing local variables." "Stack too deep, try removing local variables."
); );
// fetch target storage reference // fetch target storage reference

View File

@ -402,7 +402,7 @@ void CompilerContext::appendInlineAssembly(
stackDiff -= 1; stackDiff -= 1;
if (stackDiff < 1 || stackDiff > 16) if (stackDiff < 1 || stackDiff > 16)
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
CompilerError() << StackTooDeepError() <<
errinfo_sourceLocation(_identifier.location) << errinfo_sourceLocation(_identifier.location) <<
util::errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.") util::errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.")
); );

View File

@ -455,7 +455,11 @@ void CompilerUtils::encodeToMemory(
// leave end_of_mem as dyn head pointer // leave end_of_mem as dyn head pointer
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD; m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
dynPointers++; dynPointers++;
solAssert((argSize + dynPointers) < 16, "Stack too deep, try using fewer variables."); assertThrow(
(argSize + dynPointers) < 16,
StackTooDeepError,
"Stack too deep, try using fewer variables."
);
} }
else else
{ {
@ -497,8 +501,9 @@ void CompilerUtils::encodeToMemory(
if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace) if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
{ {
// copy tail pointer (=mem_end - mem_start) to memory // copy tail pointer (=mem_end - mem_start) to memory
solAssert( assertThrow(
(2 + dynPointers) <= 16, (2 + dynPointers) <= 16,
StackTooDeepError,
"Stack too deep(" + to_string(2 + dynPointers) + "), try using fewer variables." "Stack too deep(" + to_string(2 + dynPointers) + "), try using fewer variables."
); );
m_context << dupInstruction(2 + dynPointers) << Instruction::DUP2; m_context << dupInstruction(2 + dynPointers) << Instruction::DUP2;
@ -1241,7 +1246,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
// move variable starting from its top end in the stack // move variable starting from its top end in the stack
if (stackPosition - size + 1 > 16) if (stackPosition - size + 1 > 16)
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
CompilerError() << StackTooDeepError() <<
errinfo_sourceLocation(_variable.location()) << errinfo_sourceLocation(_variable.location()) <<
util::errinfo_comment("Stack too deep, try removing local variables.") util::errinfo_comment("Stack too deep, try removing local variables.")
); );
@ -1251,7 +1256,11 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize) void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
{ {
solAssert(_stackDepth <= 16, "Stack too deep, try removing local variables."); assertThrow(
_stackDepth <= 16,
StackTooDeepError,
"Stack too deep, try removing local variables."
);
for (unsigned i = 0; i < _itemSize; ++i) for (unsigned i = 0; i < _itemSize; ++i)
m_context << dupInstruction(_stackDepth); m_context << dupInstruction(_stackDepth);
} }
@ -1273,14 +1282,22 @@ void CompilerUtils::moveIntoStack(unsigned _stackDepth, unsigned _itemSize)
void CompilerUtils::rotateStackUp(unsigned _items) void CompilerUtils::rotateStackUp(unsigned _items)
{ {
solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables."); assertThrow(
_items - 1 <= 16,
StackTooDeepError,
"Stack too deep, try removing local variables."
);
for (unsigned i = 1; i < _items; ++i) for (unsigned i = 1; i < _items; ++i)
m_context << swapInstruction(_items - i); m_context << swapInstruction(_items - i);
} }
void CompilerUtils::rotateStackDown(unsigned _items) void CompilerUtils::rotateStackDown(unsigned _items)
{ {
solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables."); assertThrow(
_items - 1 <= 16,
StackTooDeepError,
"Stack too deep, try removing local variables."
);
for (unsigned i = 1; i < _items; ++i) for (unsigned i = 1; i < _items; ++i)
m_context << swapInstruction(i); m_context << swapInstruction(i);
} }

View File

@ -630,7 +630,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
if (stackLayout.size() > 17) if (stackLayout.size() > 17)
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
CompilerError() << StackTooDeepError() <<
errinfo_sourceLocation(_function.location()) << errinfo_sourceLocation(_function.location()) <<
errinfo_comment("Stack too deep, try removing local variables.") errinfo_comment("Stack too deep, try removing local variables.")
); );
@ -794,7 +794,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
solAssert(variable->type()->sizeOnStack() == 1, ""); solAssert(variable->type()->sizeOnStack() == 1, "");
if (stackDiff < 1 || stackDiff > 16) if (stackDiff < 1 || stackDiff > 16)
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
CompilerError() << StackTooDeepError() <<
errinfo_sourceLocation(_inlineAssembly.location()) << errinfo_sourceLocation(_inlineAssembly.location()) <<
errinfo_comment("Stack too deep, try removing local variables.") errinfo_comment("Stack too deep, try removing local variables.")
); );
@ -827,7 +827,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
int stackDiff = _assembly.stackHeight() - m_context.baseStackOffsetOfVariable(*variable) - 1; int stackDiff = _assembly.stackHeight() - m_context.baseStackOffsetOfVariable(*variable) - 1;
if (stackDiff > 16 || stackDiff < 1) if (stackDiff > 16 || stackDiff < 1)
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
CompilerError() << StackTooDeepError() <<
errinfo_sourceLocation(_inlineAssembly.location()) << errinfo_sourceLocation(_inlineAssembly.location()) <<
errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.") errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.")
); );

View File

@ -227,7 +227,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
solAssert(retSizeOnStack == utils().sizeOnStack(returnTypes), ""); solAssert(retSizeOnStack == utils().sizeOnStack(returnTypes), "");
if (retSizeOnStack > 15) if (retSizeOnStack > 15)
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
CompilerError() << StackTooDeepError() <<
errinfo_sourceLocation(_varDecl.location()) << errinfo_sourceLocation(_varDecl.location()) <<
errinfo_comment("Stack too deep.") errinfo_comment("Stack too deep.")
); );
@ -309,7 +309,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
{ {
if (itemSize + lvalueSize > 16) if (itemSize + lvalueSize > 16)
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
CompilerError() << StackTooDeepError() <<
errinfo_sourceLocation(_assignment.location()) << errinfo_sourceLocation(_assignment.location()) <<
errinfo_comment("Stack too deep, try removing local variables.") errinfo_comment("Stack too deep, try removing local variables.")
); );

View File

@ -47,7 +47,7 @@ void StackVariable::retrieveValue(SourceLocation const& _location, bool) const
unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset); unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset);
if (stackPos + 1 > 16) //@todo correct this by fetching earlier or moving to memory if (stackPos + 1 > 16) //@todo correct this by fetching earlier or moving to memory
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
CompilerError() << StackTooDeepError() <<
errinfo_sourceLocation(_location) << errinfo_sourceLocation(_location) <<
errinfo_comment("Stack too deep, try removing local variables.") errinfo_comment("Stack too deep, try removing local variables.")
); );
@ -61,7 +61,7 @@ void StackVariable::storeValue(Type const&, SourceLocation const& _location, boo
unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset) - m_size + 1; unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset) - m_size + 1;
if (stackDiff > 16) if (stackDiff > 16)
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
CompilerError() << StackTooDeepError() <<
errinfo_sourceLocation(_location) << errinfo_sourceLocation(_location) <<
errinfo_comment("Stack too deep, try removing local variables.") errinfo_comment("Stack too deep, try removing local variables.")
); );

View File

@ -98,13 +98,13 @@ message Statement {
VarDecl vd = 1; VarDecl vd = 1;
Assignment a = 2; Assignment a = 2;
UnaryOpStmt u = 3; UnaryOpStmt u = 3;
BinaryOpStmt b = 4;
} }
} }
message Block { message Block {
required VarDecl v = 1; repeated Statement s = 1;
repeated Statement s = 2; required Return r = 2;
required Return r = 3;
} }
message Program { message Program {

View File

@ -123,7 +123,7 @@ std::pair<bytes, Json::Value> compileContract(
); );
} }
// Ignore stack too deep errors during compilation // Ignore stack too deep errors during compilation
catch (Exception const&) catch (langutil::StackTooDeepError const&)
{ {
throw langutil::FuzzerError(); throw langutil::FuzzerError();
} }

View File

@ -50,7 +50,6 @@ string ProtoConverter::visit(Block const& _block)
<< '\t' << '\t'
<< '{' << '{'
<< '\n'; << '\n';
blockStr << visit(_block.v());
for (auto const& s: _block.s()) for (auto const& s: _block.s())
blockStr << visit(s); blockStr << visit(s);
blockStr << visit(_block.r()); blockStr << visit(_block.r());
@ -67,9 +66,20 @@ string ProtoConverter::visit(Statement const& _stmt)
case Statement::kVd: case Statement::kVd:
return visit(_stmt.vd()); return visit(_stmt.vd());
case Statement::kA: case Statement::kA:
return visit(_stmt.a()); if (varAvailable())
return visit(_stmt.a());
else
return "";
case Statement::kU: case Statement::kU:
return visit(_stmt.u()); if (varAvailable())
return visit(_stmt.u());
else
return "";
case Statement::kB:
if (varAvailable())
return visit(_stmt.b());
else
return "";
case Statement::STMT_ONEOF_NOT_SET: case Statement::STMT_ONEOF_NOT_SET:
return ""; return "";
} }
@ -77,14 +87,13 @@ string ProtoConverter::visit(Statement const& _stmt)
string ProtoConverter::visit(VarDecl const& _vardecl) string ProtoConverter::visit(VarDecl const& _vardecl)
{ {
bool varExists = varAvailable();
Whiskers v(R"(<type> <varName> = <type>(<value>);)"); Whiskers v(R"(<type> <varName> = <type>(<value>);)");
string type = visit(_vardecl.t()); string type = visit(_vardecl.t());
string varName = newVarName(); string varName = newVarName();
m_varTypeMap.emplace(varName, pair(typeSign(_vardecl.t()), type)); m_varTypeMap.emplace(varName, pair(typeSign(_vardecl.t()), type));
v("type", type); v("type", type);
v("varName", varName); v("varName", varName);
v("value", varExists ? visit(_vardecl.value()) : maskUnsignedToHex(64)); v("value", visit(_vardecl.value()));
incrementVarCounter(); incrementVarCounter();
return "\t\t" + v.render() + '\n'; return "\t\t" + v.render() + '\n';
} }
@ -109,6 +118,43 @@ string ProtoConverter::visit(UnaryOpStmt const& _uop)
} }
} }
string ProtoConverter::visit(BinaryOpStmt const& _bopstmt)
{
string op{};
switch (_bopstmt.op())
{
case BinaryOpStmt::ADDSELF:
op = " += ";
break;
case BinaryOpStmt::SUBSELF:
op = " -= ";
break;
case BinaryOpStmt::MULSELF:
op = " *= ";
break;
case BinaryOpStmt::DIVSELF:
op = " /= ";
break;
case BinaryOpStmt::MODSELF:
op = " %= ";
break;
case BinaryOpStmt::SHLSELF:
op = " <<= ";
break;
case BinaryOpStmt::SHRSELF:
op = " >>= ";
break;
}
string left = visit(_bopstmt.left());
string right = visit(_bopstmt.right());
string leftSignString = m_varTypeMap[left].second;
string rightSignString = m_exprSignMap[&_bopstmt.right()].second;
if (leftSignString != rightSignString)
right = leftSignString + '(' + right + ')';
return "\t\t" + left + op + right + ";\n";
}
string ProtoConverter::visit(BinaryOp const& _bop) string ProtoConverter::visit(BinaryOp const& _bop)
{ {
string op{}; string op{};
@ -163,11 +209,19 @@ string ProtoConverter::visit(Expression const& _expr)
{ {
case Expression::kV: case Expression::kV:
{ {
solAssert(varAvailable(), "Sol arith fuzzer: Varref unavaileble"); if (varAvailable())
string v = visit(_expr.v()); {
if (!m_exprSignMap.count(&_expr)) string v = visit(_expr.v());
m_exprSignMap.emplace(&_expr, m_varTypeMap[v]); if (!m_exprSignMap.count(&_expr))
return v; m_exprSignMap.emplace(&_expr, m_varTypeMap[v]);
return v;
}
else
{
if (!m_exprSignMap.count(&_expr))
m_exprSignMap.emplace(&_expr, pair(Sign::Unsigned, "uint"));
return maskUnsignedToHex(64);
}
} }
case Expression::kBop: case Expression::kBop:
{ {
@ -193,16 +247,11 @@ string ProtoConverter::visit(VarRef const& _v)
string ProtoConverter::visit(Assignment const& _assignment) string ProtoConverter::visit(Assignment const& _assignment)
{ {
if (varAvailable()) string varName = visit(_assignment.id());
{ solAssert(m_varTypeMap.count(varName), "Sol arith fuzzer: Invalid varname");
string varName = visit(_assignment.id()); Whiskers a(R"(<varName> = <type>(<expr>);)");
solAssert(m_varTypeMap.count(varName), "Sol arith fuzzer: Invalid varname"); a("varName", varName);
Whiskers a(R"(<varName> = <type>(<expr>);)"); a("type", m_varTypeMap[varName].second);
a("varName", varName); a("expr", visit(_assignment.value()));
a("type", m_varTypeMap[varName].second); return "\t\t" + a.render() + '\n';
a("expr", visit(_assignment.value()));
return "\t\t" + a.render() + '\n';
}
else
return "";
} }

View File

@ -70,6 +70,7 @@ public:
private: private:
std::string visit(Type const& _type); std::string visit(Type const& _type);
std::string visit(UnaryOpStmt const& _uop); std::string visit(UnaryOpStmt const& _uop);
std::string visit(BinaryOpStmt const& _bop);
std::string visit(BinaryOp const& _bop); std::string visit(BinaryOp const& _bop);
std::string visit(VarDecl const& _decl); std::string visit(VarDecl const& _decl);
std::string visit(Assignment const& _assignment); std::string visit(Assignment const& _assignment);