diff --git a/liblangutil/Exceptions.h b/liblangutil/Exceptions.h index 8e5081985..b12400ab0 100644 --- a/liblangutil/Exceptions.h +++ b/liblangutil/Exceptions.h @@ -38,6 +38,7 @@ class Error; using ErrorList = std::vector>; struct FuzzerError: virtual util::Exception {}; +struct StackTooDeepError: virtual util::Exception {}; struct CompilerError: virtual util::Exception {}; struct InternalCompilerError: virtual util::Exception {}; struct FatalError: virtual util::Exception {}; diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 089d0f5cf..4fb786073 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -219,8 +219,9 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons else 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] ... - solAssert( + assertThrow( 2 + byteOffsetSize + sourceBaseType->sizeOnStack() <= 16, + StackTooDeepError, "Stack too deep, try removing local variables." ); // fetch target storage reference diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 2d81e833b..b76e56c53 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -402,7 +402,7 @@ void CompilerContext::appendInlineAssembly( stackDiff -= 1; if (stackDiff < 1 || stackDiff > 16) BOOST_THROW_EXCEPTION( - CompilerError() << + StackTooDeepError() << errinfo_sourceLocation(_identifier.location) << util::errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.") ); diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 3d0117707..83467ace2 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -455,7 +455,11 @@ void CompilerUtils::encodeToMemory( // leave end_of_mem as dyn head pointer m_context << Instruction::DUP1 << u256(32) << Instruction::ADD; 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 { @@ -497,8 +501,9 @@ void CompilerUtils::encodeToMemory( if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace) { // copy tail pointer (=mem_end - mem_start) to memory - solAssert( + assertThrow( (2 + dynPointers) <= 16, + StackTooDeepError, "Stack too deep(" + to_string(2 + dynPointers) + "), try using fewer variables." ); 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 if (stackPosition - size + 1 > 16) BOOST_THROW_EXCEPTION( - CompilerError() << + StackTooDeepError() << errinfo_sourceLocation(_variable.location()) << 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) { - 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) m_context << dupInstruction(_stackDepth); } @@ -1273,14 +1282,22 @@ void CompilerUtils::moveIntoStack(unsigned _stackDepth, unsigned _itemSize) 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) m_context << swapInstruction(_items - i); } 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) m_context << swapInstruction(i); } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index d095c1eac..90f8c50dc 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -630,7 +630,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) if (stackLayout.size() > 17) BOOST_THROW_EXCEPTION( - CompilerError() << + StackTooDeepError() << errinfo_sourceLocation(_function.location()) << errinfo_comment("Stack too deep, try removing local variables.") ); @@ -794,7 +794,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) solAssert(variable->type()->sizeOnStack() == 1, ""); if (stackDiff < 1 || stackDiff > 16) BOOST_THROW_EXCEPTION( - CompilerError() << + StackTooDeepError() << errinfo_sourceLocation(_inlineAssembly.location()) << 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; if (stackDiff > 16 || stackDiff < 1) BOOST_THROW_EXCEPTION( - CompilerError() << + StackTooDeepError() << errinfo_sourceLocation(_inlineAssembly.location()) << errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.") ); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 14b0d763b..b780e9827 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -227,7 +227,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& solAssert(retSizeOnStack == utils().sizeOnStack(returnTypes), ""); if (retSizeOnStack > 15) BOOST_THROW_EXCEPTION( - CompilerError() << + StackTooDeepError() << errinfo_sourceLocation(_varDecl.location()) << errinfo_comment("Stack too deep.") ); @@ -309,7 +309,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) { if (itemSize + lvalueSize > 16) BOOST_THROW_EXCEPTION( - CompilerError() << + StackTooDeepError() << errinfo_sourceLocation(_assignment.location()) << errinfo_comment("Stack too deep, try removing local variables.") ); diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 3a04d6f92..798428098 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -47,7 +47,7 @@ void StackVariable::retrieveValue(SourceLocation const& _location, bool) const unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset); if (stackPos + 1 > 16) //@todo correct this by fetching earlier or moving to memory BOOST_THROW_EXCEPTION( - CompilerError() << + StackTooDeepError() << errinfo_sourceLocation(_location) << 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; if (stackDiff > 16) BOOST_THROW_EXCEPTION( - CompilerError() << + StackTooDeepError() << errinfo_sourceLocation(_location) << errinfo_comment("Stack too deep, try removing local variables.") ); diff --git a/test/tools/ossfuzz/solArith.proto b/test/tools/ossfuzz/solArith.proto index a64ec982f..2fed2b925 100644 --- a/test/tools/ossfuzz/solArith.proto +++ b/test/tools/ossfuzz/solArith.proto @@ -98,13 +98,13 @@ message Statement { VarDecl vd = 1; Assignment a = 2; UnaryOpStmt u = 3; + BinaryOpStmt b = 4; } } message Block { - required VarDecl v = 1; - repeated Statement s = 2; - required Return r = 3; + repeated Statement s = 1; + required Return r = 2; } message Program { diff --git a/test/tools/ossfuzz/solArithProtoFuzzer.cpp b/test/tools/ossfuzz/solArithProtoFuzzer.cpp index d65c2e775..a6669ab28 100644 --- a/test/tools/ossfuzz/solArithProtoFuzzer.cpp +++ b/test/tools/ossfuzz/solArithProtoFuzzer.cpp @@ -123,7 +123,7 @@ std::pair compileContract( ); } // Ignore stack too deep errors during compilation - catch (Exception const&) + catch (langutil::StackTooDeepError const&) { throw langutil::FuzzerError(); } diff --git a/test/tools/ossfuzz/solarithprotoToSol.cpp b/test/tools/ossfuzz/solarithprotoToSol.cpp index 444693f95..0720e24bd 100644 --- a/test/tools/ossfuzz/solarithprotoToSol.cpp +++ b/test/tools/ossfuzz/solarithprotoToSol.cpp @@ -50,7 +50,6 @@ string ProtoConverter::visit(Block const& _block) << '\t' << '{' << '\n'; - blockStr << visit(_block.v()); for (auto const& s: _block.s()) blockStr << visit(s); blockStr << visit(_block.r()); @@ -67,9 +66,20 @@ string ProtoConverter::visit(Statement const& _stmt) case Statement::kVd: return visit(_stmt.vd()); case Statement::kA: - return visit(_stmt.a()); + if (varAvailable()) + return visit(_stmt.a()); + else + return ""; 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: return ""; } @@ -77,14 +87,13 @@ string ProtoConverter::visit(Statement const& _stmt) string ProtoConverter::visit(VarDecl const& _vardecl) { - bool varExists = varAvailable(); Whiskers v(R"( = ();)"); string type = visit(_vardecl.t()); string varName = newVarName(); m_varTypeMap.emplace(varName, pair(typeSign(_vardecl.t()), type)); v("type", type); v("varName", varName); - v("value", varExists ? visit(_vardecl.value()) : maskUnsignedToHex(64)); + v("value", visit(_vardecl.value())); incrementVarCounter(); 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 op{}; @@ -163,11 +209,19 @@ string ProtoConverter::visit(Expression const& _expr) { case Expression::kV: { - solAssert(varAvailable(), "Sol arith fuzzer: Varref unavaileble"); - string v = visit(_expr.v()); - if (!m_exprSignMap.count(&_expr)) - m_exprSignMap.emplace(&_expr, m_varTypeMap[v]); - return v; + if (varAvailable()) + { + string v = visit(_expr.v()); + if (!m_exprSignMap.count(&_expr)) + 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: { @@ -193,16 +247,11 @@ string ProtoConverter::visit(VarRef const& _v) string ProtoConverter::visit(Assignment const& _assignment) { - if (varAvailable()) - { - string varName = visit(_assignment.id()); - solAssert(m_varTypeMap.count(varName), "Sol arith fuzzer: Invalid varname"); - Whiskers a(R"( = ();)"); - a("varName", varName); - a("type", m_varTypeMap[varName].second); - a("expr", visit(_assignment.value())); - return "\t\t" + a.render() + '\n'; - } - else - return ""; + string varName = visit(_assignment.id()); + solAssert(m_varTypeMap.count(varName), "Sol arith fuzzer: Invalid varname"); + Whiskers a(R"( = ();)"); + a("varName", varName); + a("type", m_varTypeMap[varName].second); + a("expr", visit(_assignment.value())); + return "\t\t" + a.render() + '\n'; } \ No newline at end of file diff --git a/test/tools/ossfuzz/solarithprotoToSol.h b/test/tools/ossfuzz/solarithprotoToSol.h index 23421b50a..79a822fe9 100644 --- a/test/tools/ossfuzz/solarithprotoToSol.h +++ b/test/tools/ossfuzz/solarithprotoToSol.h @@ -70,6 +70,7 @@ public: private: std::string visit(Type const& _type); std::string visit(UnaryOpStmt const& _uop); + std::string visit(BinaryOpStmt const& _bop); std::string visit(BinaryOp const& _bop); std::string visit(VarDecl const& _decl); std::string visit(Assignment const& _assignment);