diff options
author | chriseth <chris@ethereum.org> | 2017-03-22 20:21:58 +0800 |
---|---|---|
committer | chriseth <chris@ethereum.org> | 2017-04-25 22:49:03 +0800 |
commit | 580921b3294bc582c68db04f1ebf83b9bec206f5 (patch) | |
tree | 2ca11e27a3ce567377eea1d265628c2bdd31dc4a /libsolidity | |
parent | f13c8af3f9f3b0aec88809fbb609e183c9bba4be (diff) | |
download | dexon-solidity-580921b3294bc582c68db04f1ebf83b9bec206f5.tar.gz dexon-solidity-580921b3294bc582c68db04f1ebf83b9bec206f5.tar.zst dexon-solidity-580921b3294bc582c68db04f1ebf83b9bec206f5.zip |
Perform stack height checks in assembly analysis phase.
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/inlineasm/AsmAnalysis.cpp | 154 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmAnalysis.h | 13 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmCodeGen.cpp | 18 |
3 files changed, 148 insertions, 37 deletions
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 29016ef9..e989560d 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -51,11 +51,20 @@ bool AsmAnalyzer::analyze(Block const& _block) { if (!(ScopeFiller(m_scopes, m_errors))(_block)) return false; + return (*this)(_block); } +bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction) +{ + auto const& info = instructionInfo(_instruction.instruction); + m_stackHeight += info.ret - info.args; + return true; +} + bool AsmAnalyzer::operator()(assembly::Literal const& _literal) { + ++m_stackHeight; if (!_literal.isNumber && _literal.value.size() > 32) { m_errors.push_back(make_shared<Error>( @@ -83,8 +92,12 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) )); success = false; } + ++m_stackHeight; + }, + [&](Scope::Label const&) + { + ++m_stackHeight; }, - [&](Scope::Label const&) {}, [&](Scope::Function const&) { m_errors.push_back(make_shared<Error>( @@ -97,14 +110,21 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) ))) { } - else if (!m_resolver || m_resolver(_identifier, IdentifierContext::RValue) == size_t(-1)) + else { - m_errors.push_back(make_shared<Error>( - Error::Type::DeclarationError, - "Identifier not found.", - _identifier.location - )); - success = false; + size_t stackSize(-1); + if (m_resolver) + stackSize = m_resolver(_identifier, IdentifierContext::RValue); + if (stackSize == size_t(-1)) + { + m_errors.push_back(make_shared<Error>( + Error::Type::DeclarationError, + "Identifier not found.", + _identifier.location + )); + success = false; + } + m_stackHeight += stackSize == size_t(-1) ? 1 : stackSize; } return success; } @@ -113,8 +133,14 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) { bool success = true; for (auto const& arg: _instr.arguments | boost::adaptors::reversed) + { + int const stackHeight = m_stackHeight; if (!boost::apply_visitor(*this, arg)) success = false; + if (!expectDeposit(1, stackHeight, locationOf(arg))) + success = false; + } + // Parser already checks that the number of arguments is correct. if (!(*this)(_instr.instruction)) success = false; return success; @@ -122,13 +148,15 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) bool AsmAnalyzer::operator()(assembly::Assignment const& _assignment) { - return checkAssignment(_assignment.variableName); + return checkAssignment(_assignment.variableName, size_t(-1)); } bool AsmAnalyzer::operator()(FunctionalAssignment const& _assignment) { + int const stackHeight = m_stackHeight; bool success = boost::apply_visitor(*this, *_assignment.value); - if (!checkAssignment(_assignment.variableName)) + solAssert(m_stackHeight >= stackHeight, "Negative value size."); + if (!checkAssignment(_assignment.variableName, m_stackHeight - stackHeight)) success = false; return success; } @@ -146,7 +174,14 @@ bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef) for (auto const& var: _funDef.arguments + _funDef.returns) boost::get<Scope::Variable>(bodyScope.identifiers.at(var)).active = true; - return (*this)(_funDef.body); + int const stackHeight = m_stackHeight; + m_stackHeight = _funDef.arguments.size() + _funDef.returns.size(); + m_virtualVariablesInNextBlock = m_stackHeight; + + bool success = (*this)(_funDef.body); + + m_stackHeight = stackHeight; + return success; } bool AsmAnalyzer::operator()(assembly::FunctionCall const& _funCall) @@ -202,12 +237,16 @@ bool AsmAnalyzer::operator()(assembly::FunctionCall const& _funCall) )); success = false; } - //@todo check the number of returns - depends on context and should probably - // be only done once we have stack height checks } for (auto const& arg: _funCall.arguments | boost::adaptors::reversed) + { + int const stackHeight = m_stackHeight; if (!boost::apply_visitor(*this, arg)) success = false; + if (!expectDeposit(1, stackHeight, locationOf(arg))) + success = false; + } + m_stackHeight += returns - arguments; return success; } @@ -216,19 +255,42 @@ bool AsmAnalyzer::operator()(Block const& _block) bool success = true; m_currentScope = &scope(&_block); + int const virtualVariablesInNextBlock = m_virtualVariablesInNextBlock; + m_virtualVariablesInNextBlock = 0; + int const initialStackHeight = m_stackHeight; + for (auto const& s: _block.statements) if (!boost::apply_visitor(*this, s)) success = false; + for (auto const& identifier: scope(&_block).identifiers) + if (identifier.second.type() == typeid(Scope::Variable)) + --m_stackHeight; + + int const stackDiff = m_stackHeight - initialStackHeight + virtualVariablesInNextBlock; + if (stackDiff != 0) + { + m_errors.push_back(make_shared<Error>( + Error::Type::DeclarationError, + "Unbalanced stack at the end of a block: " + + ( + stackDiff > 0 ? + to_string(stackDiff) + string(" surplus item(s).") : + to_string(-stackDiff) + string(" missing item(s).") + ), + _block.location + )); + success = false; + } + m_currentScope = m_currentScope->superScope; return success; } -bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable) +bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t _valueSize) { - if (!(*this)(_variable)) - return false; - + bool success = true; + size_t variableSize(-1); if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name)) { // Check that it is a variable @@ -239,19 +301,69 @@ bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable) "Assignment requires variable.", _variable.location )); - return false; + success = false; } + else if (!boost::get<Scope::Variable>(*var).active) + { + m_errors.push_back(make_shared<Error>( + Error::Type::DeclarationError, + "Variable " + _variable.name + " used before it was declared.", + _variable.location + )); + success = false; + } + variableSize = 1; } - else if (!m_resolver || m_resolver(_variable, IdentifierContext::LValue) == size_t(-1)) + else if (m_resolver) + variableSize = m_resolver(_variable, IdentifierContext::LValue); + if (variableSize == size_t(-1)) { m_errors.push_back(make_shared<Error>( Error::Type::DeclarationError, - "Variable not found.", + "Variable not found or variable not lvalue.", _variable.location )); + success = false; + } + if (_valueSize == size_t(-1)) + _valueSize = variableSize == size_t(-1) ? 1 : variableSize; + + m_stackHeight -= _valueSize; + + if (_valueSize != variableSize && variableSize != size_t(-1)) + { + m_errors.push_back(make_shared<Error>( + Error::Type::TypeError, + "Variable size (" + + to_string(variableSize) + + ") and value size (" + + to_string(_valueSize) + + ") do not match.", + _variable.location + )); + success = false; + } + return success; +} + +bool AsmAnalyzer::expectDeposit(int const _deposit, int const _oldHeight, SourceLocation const& _location) +{ + int stackDiff = m_stackHeight - _oldHeight; + if (stackDiff != _deposit) + { + m_errors.push_back(make_shared<Error>( + Error::Type::TypeError, + "Expected instruction(s) to deposit " + + boost::lexical_cast<string>(_deposit) + + " item(s) to the stack, but did deposit " + + boost::lexical_cast<string>(stackDiff) + + " item(s).", + _location + )); return false; } - return true; + else + return true; } Scope& AsmAnalyzer::scope(Block const* _block) diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h index c6d23227..cfc9b25a 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libsolidity/inlineasm/AsmAnalysis.h @@ -53,7 +53,7 @@ struct Scope; /** * Performs the full analysis stage, calls the ScopeFiller internally, then resolves * references and performs other checks. - * @todo Does not yet check for stack height issues. + * If all these checks pass, code generation should not throw errors. */ class AsmAnalyzer: public boost::static_visitor<bool> { @@ -63,7 +63,7 @@ public: bool analyze(assembly::Block const& _block); - bool operator()(assembly::Instruction const&) { return true; } + bool operator()(assembly::Instruction const&); bool operator()(assembly::Literal const& _literal); bool operator()(assembly::Identifier const&); bool operator()(assembly::FunctionalInstruction const& _functionalInstruction); @@ -76,9 +76,16 @@ public: bool operator()(assembly::Block const& _block); private: - bool checkAssignment(assembly::Identifier const& _assignment); + /// Verifies that a variable to be assigned to exists and has the same size + /// as the value, @a _valueSize, unless that is equal to -1. + bool checkAssignment(assembly::Identifier const& _assignment, size_t _valueSize = size_t(-1)); + bool expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location); Scope& scope(assembly::Block const* _block); + /// Number of excess stack slots generated by function arguments to take into account for + /// next block. + int m_virtualVariablesInNextBlock = 0; + int m_stackHeight = 0; ExternalIdentifierAccess::Resolver const& m_resolver; Scope* m_currentScope = nullptr; Scopes& m_scopes; diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index c1efa292..a79c5a37 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -185,7 +185,7 @@ public: { int height = m_state.assembly.deposit(); boost::apply_visitor(*this, *it); - expectDeposit(1, height, locationOf(*it)); + expectDeposit(1, height); } (*this)(_instr.instruction); } @@ -210,7 +210,7 @@ public: { int height = m_state.assembly.deposit(); boost::apply_visitor(*this, *_assignment.value); - expectDeposit(1, height, locationOf(*_assignment.value)); + expectDeposit(1, height); m_state.assembly.setSourceLocation(_assignment.location); generateAssignment(_assignment.variableName, _assignment.location); } @@ -218,7 +218,7 @@ public: { int height = m_state.assembly.deposit(); boost::apply_visitor(*this, *_varDecl.value); - expectDeposit(1, height, locationOf(*_varDecl.value)); + expectDeposit(1, height); auto& var = boost::get<Scope::Variable>(m_scope.identifiers.at(_varDecl.name)); var.stackHeight = height; var.active = true; @@ -279,17 +279,9 @@ private: return heightDiff; } - void expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location) + void expectDeposit(int _deposit, int _oldHeight) { - if (m_state.assembly.deposit() != _oldHeight + 1) - m_state.addError(Error::Type::TypeError, - "Expected instruction(s) to deposit " + - boost::lexical_cast<string>(_deposit) + - " item(s) to the stack, but did deposit " + - boost::lexical_cast<string>(m_state.assembly.deposit() - _oldHeight) + - " item(s).", - _location - ); + solAssert(m_state.assembly.deposit() == _oldHeight + _deposit, "Invalid stack deposit."); } /// Assigns the label's id to a value taken from eth::Assembly if it has not yet been set. |