diff --git a/libsolidity/analysis/DocStringTagParser.cpp b/libsolidity/analysis/DocStringTagParser.cpp index 5dc68d97f..7be9cf8df 100644 --- a/libsolidity/analysis/DocStringTagParser.cpp +++ b/libsolidity/analysis/DocStringTagParser.cpp @@ -202,7 +202,7 @@ bool DocStringTagParser::visit(InlineAssembly const& _assembly) if (valuesSeen.insert(value).second) { if (value == "memory-safe-assembly") - _assembly.annotation().memorySafe = true; + _assembly.annotation().markedMemorySafe = true; else m_errorReporter.warning( 8787_error, diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 74701bde0..955c66f84 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -763,6 +763,7 @@ void TypeChecker::endVisit(FunctionTypeName const& _funType) bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { + bool lvalueAccessToMemoryVariable = false; // External references have already been resolved in a prior stage and stored in the annotation. // We run the resolve step again regardless. yul::ExternalIdentifierAccess::Resolver identifierAccess = [&]( @@ -787,6 +788,8 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) if (auto var = dynamic_cast(declaration)) { solAssert(var->type(), "Expected variable type!"); + if (_context == yul::IdentifierContext::LValue && var->type()->dataStoredIn(DataLocation::Memory)) + lvalueAccessToMemoryVariable = true; if (var->immutable()) { m_errorReporter.typeError(3773_error, nativeLocationOf(_identifier), "Assembly access to immutable variables is not supported."); @@ -974,8 +977,11 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) identifierAccess ); if (!analyzer.analyze(_inlineAssembly.operations())) - return false; - return true; + solAssert(m_errorReporter.hasErrors()); + _inlineAssembly.annotation().hasMemoryEffects = + lvalueAccessToMemoryVariable || + (analyzer.sideEffects().memory != yul::SideEffects::None); + return false; } bool TypeChecker::visit(IfStatement const& _ifStatement) diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 4489dce8d..0d6901ef2 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -221,7 +221,9 @@ struct InlineAssemblyAnnotation: StatementAnnotation /// Information generated during analysis phase. std::shared_ptr analysisInfo; /// True, if the assembly block was annotated to be memory-safe. - bool memorySafe = false; + bool markedMemorySafe = false; + /// True, if the assembly block involves any memory opcode or assigns to variables in memory. + SetOnce hasMemoryEffects; }; struct BlockAnnotation: StatementAnnotation, ScopableAnnotation diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index cf0977c6e..263f90261 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -2138,7 +2138,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) { setLocation(_inlineAsm); - if (!_inlineAsm.annotation().memorySafe) + if (*_inlineAsm.annotation().hasMemoryEffects && !_inlineAsm.annotation().markedMemorySafe) m_context.setMemoryUnsafeInlineAssemblySeen(); CopyTranslate bodyCopier{_inlineAsm.dialect(), m_context, _inlineAsm.annotation().externalReferences}; diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index d19aa1a4e..c38c0ba8e 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -316,6 +316,7 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) literalArguments = &f->literalArguments; validateInstructions(_funCall); + m_sideEffects += f->sideEffects; } else if (m_currentScope->lookup(_funCall.functionName.name, GenericVisitor{ [&](Scope::Variable const&) diff --git a/libyul/AsmAnalysis.h b/libyul/AsmAnalysis.h index dc2770500..c71b4cbe3 100644 --- a/libyul/AsmAnalysis.h +++ b/libyul/AsmAnalysis.h @@ -94,6 +94,8 @@ public: void operator()(Leave const&) { } void operator()(Block const& _block); + /// @returns the worst side effects encountered during analysis (including within defined functions). + SideEffects const& sideEffects() const { return m_sideEffects; } private: /// Visits the expression, expects that it evaluates to exactly one value and /// returns the type. Reports errors on errors and returns the default type. @@ -128,6 +130,8 @@ private: /// Names of data objects to be referenced by builtin functions with literal arguments. std::set m_dataNames; ForLoop const* m_currentForLoop = nullptr; + /// Worst side effects encountered during analysis (including within defined functions). + SideEffects m_sideEffects; }; }