From e6848caac1246650ac5108f9abd08ac877b0f592 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 3 Feb 2022 11:49:16 +0100 Subject: [PATCH 1/6] Allow annotating inline assembly as memory-safe. --- Changelog.md | 1 + libsolidity/analysis/DocStringTagParser.cpp | 66 +++++++++++++++++++ libsolidity/analysis/DocStringTagParser.h | 1 + libsolidity/ast/ASTAnnotations.h | 2 + libsolidity/codegen/ir/IRGenerationContext.h | 8 +-- libsolidity/codegen/ir/IRGenerator.cpp | 8 +-- .../codegen/ir/IRGeneratorForStatements.cpp | 3 +- .../inlineAssembly/invalid_natspec.sol | 14 ++++ .../inlineAssembly/natspec_memory_safe.sol | 6 ++ 9 files changed, 100 insertions(+), 9 deletions(-) create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/invalid_natspec.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/natspec_memory_safe.sol diff --git a/Changelog.md b/Changelog.md index fd7dda584..181c386aa 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.8.13 (unreleased) Language Features: + * General: Allow annotating inline assembly as memory-safe to allow optimizations and stack limit evasion that rely on respecting Solidity's memory model. Compiler Features: diff --git a/libsolidity/analysis/DocStringTagParser.cpp b/libsolidity/analysis/DocStringTagParser.cpp index 137cdb409..5dc68d97f 100644 --- a/libsolidity/analysis/DocStringTagParser.cpp +++ b/libsolidity/analysis/DocStringTagParser.cpp @@ -30,6 +30,7 @@ #include #include +#include #include @@ -162,6 +163,71 @@ bool DocStringTagParser::visit(ErrorDefinition const& _error) return true; } +bool DocStringTagParser::visit(InlineAssembly const& _assembly) +{ + if (!_assembly.documentation()) + return true; + StructuredDocumentation documentation{-1, _assembly.location(), _assembly.documentation()}; + ErrorList errors; + ErrorReporter errorReporter{errors}; + auto docTags = DocStringParser{documentation, errorReporter}.parse(); + + if (!errors.empty()) + { + SecondarySourceLocation ssl; + for (auto const& error: errors) + if (error->comment()) + ssl.append( + *error->comment(), + _assembly.location() + ); + m_errorReporter.warning( + 7828_error, + _assembly.location(), + "Inline assembly has invalid NatSpec documentation.", + ssl + ); + } + + for (auto const& [tagName, tagValue]: docTags) + { + if (tagName == "solidity") + { + vector values; + boost::split(values, tagValue.content, isWhiteSpace); + + set valuesSeen; + set duplicates; + for (auto const& value: values | ranges::views::filter(not_fn(&string::empty))) + if (valuesSeen.insert(value).second) + { + if (value == "memory-safe-assembly") + _assembly.annotation().memorySafe = true; + else + m_errorReporter.warning( + 8787_error, + _assembly.location(), + "Unexpected value for @solidity tag in inline assembly: " + value + ); + } + else if (duplicates.insert(value).second) + m_errorReporter.warning( + 4377_error, + _assembly.location(), + "Value for @solidity tag in inline assembly specified multiple times: " + value + ); + } + else + m_errorReporter.warning( + 6269_error, + _assembly.location(), + "Unexpected NatSpec tag \"" + tagName + "\" with value \"" + tagValue.content + "\" in inline assembly." + ); + } + + return true; +} + void DocStringTagParser::checkParameters( CallableDeclaration const& _callable, StructurallyDocumented const& _node, diff --git a/libsolidity/analysis/DocStringTagParser.h b/libsolidity/analysis/DocStringTagParser.h index 84fde6c6c..248befcb8 100644 --- a/libsolidity/analysis/DocStringTagParser.h +++ b/libsolidity/analysis/DocStringTagParser.h @@ -48,6 +48,7 @@ private: bool visit(ModifierDefinition const& _modifier) override; bool visit(EventDefinition const& _event) override; bool visit(ErrorDefinition const& _error) override; + bool visit(InlineAssembly const& _assembly) override; void checkParameters( CallableDeclaration const& _callable, diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 53931e426..4489dce8d 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -220,6 +220,8 @@ struct InlineAssemblyAnnotation: StatementAnnotation std::map externalReferences; /// Information generated during analysis phase. std::shared_ptr analysisInfo; + /// True, if the assembly block was annotated to be memory-safe. + bool memorySafe = false; }; struct BlockAnnotation: StatementAnnotation, ScopableAnnotation diff --git a/libsolidity/codegen/ir/IRGenerationContext.h b/libsolidity/codegen/ir/IRGenerationContext.h index cf01dc4f4..ea960bb70 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.h +++ b/libsolidity/codegen/ir/IRGenerationContext.h @@ -160,8 +160,8 @@ public: std::set& subObjectsCreated() { return m_subObjects; } - bool inlineAssemblySeen() const { return m_inlineAssemblySeen; } - void setInlineAssemblySeen() { m_inlineAssemblySeen = true; } + bool memoryUnsafeInlineAssemblySeen() const { return m_memoryUnsafeInlineAssemblySeen; } + void setMemoryUnsafeInlineAssemblySeen() { m_memoryUnsafeInlineAssemblySeen = true; } /// @returns the runtime ID to be used for the function in the dispatch routine /// and for internal function pointers. @@ -202,8 +202,8 @@ private: /// Whether to use checked or wrapping arithmetic. Arithmetic m_arithmetic = Arithmetic::Checked; - /// Flag indicating whether any inline assembly block was seen. - bool m_inlineAssemblySeen = false; + /// Flag indicating whether any memory-unsafe inline assembly block was seen. + bool m_memoryUnsafeInlineAssemblySeen = false; /// Function definitions queued for code generation. They're the Solidity functions whose calls /// were discovered by the IR generator during AST traversal. diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 6b52010d9..cb187b424 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -213,8 +213,8 @@ string IRGenerator::generate( t("subObjects", subObjectSources(m_context.subObjectsCreated())); // This has to be called only after all other code generation for the creation object is complete. - bool creationInvolvesAssembly = m_context.inlineAssemblySeen(); - t("memoryInitCreation", memoryInit(!creationInvolvesAssembly)); + bool creationInvolvesMemoryUnsafeAssembly = m_context.memoryUnsafeInlineAssemblySeen(); + t("memoryInitCreation", memoryInit(!creationInvolvesMemoryUnsafeAssembly)); t("useSrcMapCreation", formatUseSrcMap(m_context)); resetContext(_contract, ExecutionContext::Deployed); @@ -239,8 +239,8 @@ string IRGenerator::generate( t("useSrcMapDeployed", formatUseSrcMap(m_context)); // This has to be called only after all other code generation for the deployed object is complete. - bool deployedInvolvesAssembly = m_context.inlineAssemblySeen(); - t("memoryInitDeployed", memoryInit(!deployedInvolvesAssembly)); + bool deployedInvolvesMemoryUnsafeAssembly = m_context.memoryUnsafeInlineAssemblySeen(); + t("memoryInitDeployed", memoryInit(!deployedInvolvesMemoryUnsafeAssembly)); solAssert(_contract.annotation().creationCallGraph->get() != nullptr, ""); solAssert(_contract.annotation().deployedCallGraph->get() != nullptr, ""); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 9fb73f021..cf0977c6e 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -2138,7 +2138,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) { setLocation(_inlineAsm); - m_context.setInlineAssemblySeen(); + if (!_inlineAsm.annotation().memorySafe) + m_context.setMemoryUnsafeInlineAssemblySeen(); CopyTranslate bodyCopier{_inlineAsm.dialect(), m_context, _inlineAsm.annotation().externalReferences}; yul::Statement modified = bodyCopier(_inlineAsm.operations()); diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid_natspec.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid_natspec.sol new file mode 100644 index 000000000..3a6ccefb3 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid_natspec.sol @@ -0,0 +1,14 @@ +contract C { + function f() public pure { + /// @test test + assembly {} + /// @solidity test + assembly {} + /// @param + assembly {} + } +} +// ---- +// Warning 6269: (60-71): Unexpected natspec tag in inline assembly: test +// Warning 8787: (95-106): Unexpected value for @solidity tag in inline assembly: test +// Warning 7828: (122-133): Inline assembly has invalid natspec documentation. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/natspec_memory_safe.sol b/test/libsolidity/syntaxTests/inlineAssembly/natspec_memory_safe.sol new file mode 100644 index 000000000..b93b20318 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/natspec_memory_safe.sol @@ -0,0 +1,6 @@ +contract C { + function f() public pure { + // @solidity memory-safe-assembly + assembly {} + } +} \ No newline at end of file From 9bcfcc61a771c3edd7e42d5ea545369415d5f22d Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 10 Feb 2022 17:57:27 +0100 Subject: [PATCH 2/6] Inline assembly without memory effects is implicitly memory safe. --- libsolidity/analysis/DocStringTagParser.cpp | 2 +- libsolidity/analysis/TypeChecker.cpp | 10 ++++++++-- libsolidity/ast/ASTAnnotations.h | 4 +++- libsolidity/codegen/ir/IRGeneratorForStatements.cpp | 2 +- libyul/AsmAnalysis.cpp | 1 + libyul/AsmAnalysis.h | 4 ++++ 6 files changed, 18 insertions(+), 5 deletions(-) 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; }; } From dfb7bf2853cfbe2f0a44f4665cbc5ed6201dbe5c Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 3 Feb 2022 12:37:40 +0100 Subject: [PATCH 3/6] Report memoryguard in stack too deep error. --- libyul/backends/evm/EVMObjectCompiler.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/libyul/backends/evm/EVMObjectCompiler.cpp b/libyul/backends/evm/EVMObjectCompiler.cpp index bede32d17..c3c95d8bc 100644 --- a/libyul/backends/evm/EVMObjectCompiler.cpp +++ b/libyul/backends/evm/EVMObjectCompiler.cpp @@ -25,6 +25,8 @@ #include #include +#include + #include #include @@ -74,7 +76,22 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize) OptimizedEVMCodeTransform::UseNamedLabels::ForFirstFunctionOfEachName ); if (!stackErrors.empty()) - BOOST_THROW_EXCEPTION(stackErrors.front()); + { + vector memoryGuardCalls = FunctionCallFinder::run( + *_object.code, + "memoryguard"_yulstring + ); + auto stackError = stackErrors.front(); + string msg = stackError.comment() ? *stackError.comment() : ""; + if (memoryGuardCalls.empty()) + msg += "\nNo memoryguard was present. " + "Consider using memory-safe assembly only and annotating it via " + "\"/// @solidity memory-safe-assembly\"."; + else + msg += "\nmemoryguard was present."; + stackError << util::errinfo_comment(msg); + BOOST_THROW_EXCEPTION(stackError); + } } else { From 62a997ac97130ee63d0a60145f7035e3947d4de6 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 3 Feb 2022 16:08:32 +0100 Subject: [PATCH 4/6] Documentation. --- docs/assembly.rst | 96 ++++++++++++++++++++++++++++++++++++ docs/ir-breaking-changes.rst | 2 + 2 files changed, 98 insertions(+) diff --git a/docs/assembly.rst b/docs/assembly.rst index e6dc6519c..b0ce0e524 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -228,6 +228,11 @@ of their block is reached. Conventions in Solidity ----------------------- +.. _assembly-typed-variables: + +Values of Typed Variables +========================= + In contrast to EVM assembly, Solidity has types which are narrower than 256 bits, e.g. ``uint24``. For efficiency, most arithmetic operations ignore the fact that types can be shorter than 256 @@ -237,6 +242,11 @@ This means that if you access such a variable from within inline assembly, you might have to manually clean the higher-order bits first. +.. _assembly-memory-management: + +Memory Management +================= + Solidity manages memory in the following way. There is a "free memory pointer" at position ``0x40`` in memory. If you want to allocate memory, use the memory starting from where this pointer points at and update it. @@ -268,3 +278,89 @@ first slot of the array and followed by the array elements. Statically-sized memory arrays do not have a length field, but it might be added later to allow better convertibility between statically- and dynamically-sized arrays, so do not rely on this. + +Memory Safety +============= + +Without the use of inline assembly, the compiler can rely on memory to remain in a well-defined +state at all times. This is especially relevant for :ref:`the new code generation pipeline via Yul IR `: +this code generation path can move local variables from stack to memory to avoid stack-too-deep errors and +perform additional memory optimizations, if it can rely on certain assumptions about memory use. + +While we recommend to always respect Solidity's memory model, inline assembly allows you to use memory +in an incompatible way. Therefore, moving stack variables to memory and additional memory optimizations are, +by default, disabled in the presence of any inline assembly block that contains a memory operation or assigns +to solidity variables in memory. + +However, you can specifically annotate an assembly block to indicate that it in fact respects Solidity's memory +model as follows: + +.. code-block:: solidity + + /// @solidity memory-safe-assembly + assembly { + ... + } + +In particular, a memory-safe assembly block may only access the following memory ranges: + +- Memory allocated by yourself using a mechanism like the ``allocate`` function described above. +- Memory allocated by Solidity, e.g. memory within the bounds of a memory array you reference. +- The scratch space between memory offset 0 and 64 mentioned above. +- Temporary memory that is located *after* the value of the free memory pointer at the beginning of the assembly block, + i.e. memory that is "allocated" at the free memory pointer without updating the free memory pointer. + +Furthermore, if the assembly block assigns to Solidity variables in memory, you need to assure that accesses to +the Solidity variables only access these memory ranges. + +Since this is mainly about the optimizer, these restrictions still need to be followed, even if the assembly block +reverts or terminates. As an example, the following assembly snippet is not memory safe: + +.. code-block:: solidity + + assembly { + returndatacopy(0, 0, returndatasize()) + revert(0, returndatasize()) + } + +But the following is: + +.. code-block:: solidity + + /// @solidity memory-safe-assembly + assembly { + let p := mload(0x40) + returndatacopy(p, 0, returndatasize()) + revert(p, returndatasize()) + } + +Note that you do not need to update the free memory pointer if there is no following allocation, +but you can only use memory starting from the current offset given by the free memory pointer. + +If the memory operations use a length of zero, it is also fine to just use any offset (not only if it falls into the scratch space): + +.. code-block:: solidity + + /// @solidity memory-safe-assembly + assembly { + revert(0, 0) + } + +Note that not only memory operations in inline assembly itself can be memory-unsafe, but also assignments to +solidity variables of reference type in memory. For example the following is not memory-safe: + +.. code-block:: solidity + + bytes memory x; + assembly { + x := 0x40 + } + x[0x20] = 0x42; + +Inline assembly that neither involves any operations that access memory nor assigns to any solidity variables +in memory is automatically considered memory-safe and does not need to be annotated. + +.. warning:: + It is your responsibility to make sure that the assembly actually satisfies the memory model. If you annotate + an assembly block as memory-safe, but violate one of the memory assumptions, this **will** lead to incorrect and + undefined behaviour that cannot easily be discovered by testing. diff --git a/docs/ir-breaking-changes.rst b/docs/ir-breaking-changes.rst index c1f6deb57..3fce53b3b 100644 --- a/docs/ir-breaking-changes.rst +++ b/docs/ir-breaking-changes.rst @@ -1,6 +1,8 @@ .. index: ir breaking changes +.. _ir-breaking-changes: + ********************************* Solidity IR-based Codegen Changes ********************************* From ad13062978beedd9960e81875ee7c9c878d78b40 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 3 Feb 2022 12:00:06 +0100 Subject: [PATCH 5/6] Patch external tests with a safe inline assembly annotation. --- test/externalTests/ens.sh | 4 +++- test/externalTests/trident.sh | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/test/externalTests/ens.sh b/test/externalTests/ens.sh index 734c5d8ae..e4edbf46c 100755 --- a/test/externalTests/ens.sh +++ b/test/externalTests/ens.sh @@ -48,7 +48,7 @@ function ens_test "${compile_only_presets[@]}" #ir-no-optimize # Compilation fails with "YulException: Variable var__945 is 1 slot(s) too deep inside the stack." #ir-optimize-evm-only # Compilation fails with "YulException: Variable var__945 is 1 slot(s) too deep inside the stack." - #ir-optimize-evm+yul # Compilation fails with "YulException: Variable _5 is 1 too deep in the stack [ _5 usr$i usr$h _7 usr$scratch usr$k usr$f _4 usr$len usr$j_2 RET _2 _1 var_data_mpos usr$totallen usr$x _12 ]" + ir-optimize-evm+yul # Needs memory-safe inline assembly patch legacy-optimize-evm-only legacy-optimize-evm+yul ) @@ -68,6 +68,8 @@ function ens_test replace_version_pragmas neutralize_packaged_contracts + find . -name "*.sol" -exec sed -i -e 's/^\(\s*\)\(assembly\)/\1\/\/\/ @solidity memory-safe-assembly\n\1\2/' '{}' \; + for preset in $SELECTED_PRESETS; do hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn store_benchmark_report hardhat ens "$repo" "$preset" diff --git a/test/externalTests/trident.sh b/test/externalTests/trident.sh index 5e230560d..6c1f56fbb 100755 --- a/test/externalTests/trident.sh +++ b/test/externalTests/trident.sh @@ -56,7 +56,7 @@ function trident_test "${compile_only_presets[@]}" #ir-no-optimize # Compilation fails with: "YulException: Variable var_amount_165 is 9 slot(s) too deep inside the stack." #ir-optimize-evm-only # Compilation fails with: "YulException: Variable var_amount_165 is 9 slot(s) too deep inside the stack." - #ir-optimize-evm+yul # Compilation fails with: "YulException: Cannot swap Variable var_nearestTick with Variable _4: too deep in the stack by 4 slots" + ir-optimize-evm+yul # Needs memory-safe inline assembly patch legacy-no-optimize legacy-optimize-evm-only legacy-optimize-evm+yul @@ -87,6 +87,7 @@ function trident_test sed -i 's|uint32(-1)|type(uint32).max|g' contracts/flat/BentoBoxV1Flat.sol sed -i 's|IERC20(0)|IERC20(address(0))|g' contracts/flat/BentoBoxV1Flat.sol sed -i 's|IStrategy(0)|IStrategy(address(0))|g' contracts/flat/BentoBoxV1Flat.sol + find contracts -name "*.sol" -exec sed -i -e 's/^\(\s*\)\(assembly\)/\1\/\/\/ @solidity memory-safe-assembly\n\1\2/' '{}' \; # @sushiswap/core package contains contracts that get built with 0.6.12 and fail our compiler # version check. It's not used by tests so we can remove it. From 6b6e163be5adec74d39f3f8e39739b658988381c Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 10 Feb 2022 17:57:43 +0100 Subject: [PATCH 6/6] Tests. --- test/CMakeLists.txt | 2 + test/InteractiveTests.h | 2 + .../constant_optimizer_yul/output | 11 +-- .../input.sol | 2 +- .../output | 7 +- .../input.sol | 2 +- .../output | 6 +- test/libsolidity/MemoryGuardTest.cpp | 78 +++++++++++++++++++ test/libsolidity/MemoryGuardTest.h | 53 +++++++++++++ .../constructor_safe_deploy_unsafe.sol | 17 ++++ .../constructor_unsafe_deploy_safe.sol | 17 ++++ .../memoryGuardTests/free_function.sol | 29 +++++++ .../memoryGuardTests/multi_annotation.sol | 17 ++++ .../memoryGuardTests/multiple_contracts.sol | 25 ++++++ .../safe_and_unmarked_empty.sol | 10 +++ .../safe_and_unmarked_unsafe.sol | 10 +++ test/libsolidity/memoryGuardTests/stub.sol | 4 + .../unmarked_but_no_memory_access.sol | 8 ++ .../unmarked_with_memory_access.sol | 8 ++ .../unmarked_with_memory_assignment.sol | 11 +++ .../externalContracts/prbmath_unsigned.sol | 2 +- .../inlineAssembly/invalid_natspec.sol | 4 +- .../inlineAssembly/natspec_multi.sol | 23 ++++++ .../natspec_multi_swallowed.sol | 19 +++++ test/tools/CMakeLists.txt | 1 + 25 files changed, 354 insertions(+), 14 deletions(-) create mode 100644 test/libsolidity/MemoryGuardTest.cpp create mode 100644 test/libsolidity/MemoryGuardTest.h create mode 100644 test/libsolidity/memoryGuardTests/constructor_safe_deploy_unsafe.sol create mode 100644 test/libsolidity/memoryGuardTests/constructor_unsafe_deploy_safe.sol create mode 100644 test/libsolidity/memoryGuardTests/free_function.sol create mode 100644 test/libsolidity/memoryGuardTests/multi_annotation.sol create mode 100644 test/libsolidity/memoryGuardTests/multiple_contracts.sol create mode 100644 test/libsolidity/memoryGuardTests/safe_and_unmarked_empty.sol create mode 100644 test/libsolidity/memoryGuardTests/safe_and_unmarked_unsafe.sol create mode 100644 test/libsolidity/memoryGuardTests/stub.sol create mode 100644 test/libsolidity/memoryGuardTests/unmarked_but_no_memory_access.sol create mode 100644 test/libsolidity/memoryGuardTests/unmarked_with_memory_access.sol create mode 100644 test/libsolidity/memoryGuardTests/unmarked_with_memory_assignment.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/natspec_multi.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/natspec_multi_swallowed.sol diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1ca9c3328..255218c15 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -83,6 +83,8 @@ set(libsolidity_sources libsolidity/InlineAssembly.cpp libsolidity/LibSolc.cpp libsolidity/Metadata.cpp + libsolidity/MemoryGuardTest.cpp + libsolidity/MemoryGuardTest.h libsolidity/SemanticTest.cpp libsolidity/SemanticTest.h libsolidity/SemVerMatcher.cpp diff --git a/test/InteractiveTests.h b/test/InteractiveTests.h index d8f3ef07e..d359a815a 100644 --- a/test/InteractiveTests.h +++ b/test/InteractiveTests.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,7 @@ Testsuite const g_interactiveTestsuites[] = { {"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create}, {"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create}, {"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create}, + {"Memory Guard Tests", "libsolidity", "memoryGuardTests", false, false, &MemoryGuardTest::create}, {"Ewasm Translation", "libyul", "ewasmTranslationTests", false, false, &yul::test::EwasmTranslationTest::create} }; diff --git a/test/cmdlineTests/constant_optimizer_yul/output b/test/cmdlineTests/constant_optimizer_yul/output index 328ac2544..2a769811a 100644 --- a/test/cmdlineTests/constant_optimizer_yul/output +++ b/test/cmdlineTests/constant_optimizer_yul/output @@ -11,14 +11,15 @@ object "C_12" { code { { /// @src 0:61:418 "contract C {..." - mstore(64, 128) + let _1 := memoryguard(0x80) + mstore(64, _1) if callvalue() { revert(0, 0) } /// @src 0:103:238 "assembly {..." sstore(0, shl(180, 1)) /// @src 0:61:418 "contract C {..." - let _1 := datasize("C_12_deployed") - codecopy(128, dataoffset("C_12_deployed"), _1) - return(128, _1) + let _2 := datasize("C_12_deployed") + codecopy(_1, dataoffset("C_12_deployed"), _2) + return(_1, _2) } } /// @use-src 0:"constant_optimizer_yul/input.sol" @@ -26,7 +27,7 @@ object "C_12" { code { { /// @src 0:61:418 "contract C {..." - mstore(64, 128) + mstore(64, memoryguard(0x80)) if callvalue() { revert(0, 0) } /// @src 0:279:410 "assembly {..." sstore(0, 0x1000000000000000000000000000000000000000000000) diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/input.sol b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/input.sol index aca9b3bef..67e1632e9 100644 --- a/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/input.sol +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/input.sol @@ -3,6 +3,6 @@ pragma solidity >=0.0.0; pragma abicoder v2; contract D { - constructor() { assembly {}} + constructor() { assembly { mstore(0,0) } } function f() public pure {} } diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output index 34b72f8b8..d9b769a12 100644 --- a/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output @@ -10,9 +10,12 @@ Optimized IR: object "D_12" { code { { - /// @src 0:82:161 "contract D {..." + /// @src 0:82:175 "contract D {..." mstore(64, 128) if callvalue() { revert(0, 0) } + /// @src 0:115:139 "assembly { mstore(0,0) }" + mstore(0, 0) + /// @src 0:82:175 "contract D {..." let _1 := datasize("D_12_deployed") codecopy(128, dataoffset("D_12_deployed"), _1) return(128, _1) @@ -22,7 +25,7 @@ object "D_12" { object "D_12_deployed" { code { { - /// @src 0:82:161 "contract D {..." + /// @src 0:82:175 "contract D {..." let _1 := memoryguard(0x80) mstore(64, _1) if iszero(lt(calldatasize(), 4)) diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/input.sol b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/input.sol index e217f9cbb..26c0bfa98 100644 --- a/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/input.sol +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/input.sol @@ -4,6 +4,6 @@ pragma abicoder v2; contract D { function f() public pure { - assembly {} + assembly { mstore(0,0) } } } diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output index cc95451e6..9a07c7861 100644 --- a/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output @@ -10,7 +10,7 @@ Optimized IR: object "D_8" { code { { - /// @src 0:82:153 "contract D {..." + /// @src 0:82:166 "contract D {..." let _1 := memoryguard(0x80) mstore(64, _1) if callvalue() { revert(0, 0) } @@ -23,7 +23,7 @@ object "D_8" { object "D_8_deployed" { code { { - /// @src 0:82:153 "contract D {..." + /// @src 0:82:166 "contract D {..." mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -32,6 +32,8 @@ object "D_8" { { if callvalue() { revert(_1, _1) } if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } + /// @src 0:134:158 "assembly { mstore(0,0) }" + mstore(/** @src 0:82:166 "contract D {..." */ _1, _1) return(128, _1) } } diff --git a/test/libsolidity/MemoryGuardTest.cpp b/test/libsolidity/MemoryGuardTest.cpp new file mode 100644 index 000000000..b757e673f --- /dev/null +++ b/test/libsolidity/MemoryGuardTest.cpp @@ -0,0 +1,78 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::util; +using namespace solidity::util::formatting; +using namespace solidity::langutil; +using namespace solidity::frontend; +using namespace solidity::frontend::test; +using namespace yul; + +TestCase::TestResult MemoryGuardTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) +{ + compiler().reset(); + compiler().setSources(StringMap{{"", m_source}}); + compiler().setViaIR(true); + compiler().setOptimiserSettings(OptimiserSettings::none()); + if (!compiler().compile()) + return TestResult::FatalError; + + m_obtainedResult.clear(); + for (string contractName: compiler().contractNames()) + { + ErrorList errors; + auto [object, analysisInfo] = yul::test::parse( + compiler().yulIR(contractName), + EVMDialect::strictAssemblyForEVMObjects({}), + errors + ); + + if (!object || !analysisInfo || Error::containsErrors(errors)) + { + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing IR." << endl; + return TestResult::FatalError; + } + + auto handleObject = [&](std::string const& _kind, Object const& _object) { + m_obtainedResult += contractName + "(" + _kind + ") " + (FunctionCallFinder::run( + *_object.code, + "memoryguard"_yulstring + ).empty() ? "false" : "true") + "\n"; + }; + handleObject("creation", *object); + size_t deployedIndex = object->subIndexByName.at( + YulString(IRNames::deployedObject(compiler().contractDefinition(contractName))) + ); + handleObject("runtime", dynamic_cast(*object->subObjects[deployedIndex])); + } + return checkResult(_stream, _linePrefix, _formatted); +} diff --git a/test/libsolidity/MemoryGuardTest.h b/test/libsolidity/MemoryGuardTest.h new file mode 100644 index 000000000..e2ca8a6e5 --- /dev/null +++ b/test/libsolidity/MemoryGuardTest.h @@ -0,0 +1,53 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace solidity::frontend::test +{ + +using solidity::test::SyntaxTestError; + +class MemoryGuardTest: public AnalysisFramework, public TestCase +{ +public: + static std::unique_ptr create(Config const& _config) + { + return std::make_unique(_config.filename); + } + MemoryGuardTest(std::string const& _filename): TestCase(_filename) + { + m_source = m_reader.source(); + m_expectation = m_reader.simpleExpectations(); + } + + TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; +}; + +} diff --git a/test/libsolidity/memoryGuardTests/constructor_safe_deploy_unsafe.sol b/test/libsolidity/memoryGuardTests/constructor_safe_deploy_unsafe.sol new file mode 100644 index 000000000..2ab905ad8 --- /dev/null +++ b/test/libsolidity/memoryGuardTests/constructor_safe_deploy_unsafe.sol @@ -0,0 +1,17 @@ +contract C { + constructor() { + uint256 x; + assembly { x := 0 } + f(); + } + function f() internal pure { + /// @solidity memory-safe-assembly + assembly { mstore(0, 0) } + } + function g() public pure { + assembly { mstore(0, 0) } + } +} +// ---- +// :C(creation) true +// :C(runtime) false diff --git a/test/libsolidity/memoryGuardTests/constructor_unsafe_deploy_safe.sol b/test/libsolidity/memoryGuardTests/constructor_unsafe_deploy_safe.sol new file mode 100644 index 000000000..12b7e772f --- /dev/null +++ b/test/libsolidity/memoryGuardTests/constructor_unsafe_deploy_safe.sol @@ -0,0 +1,17 @@ +contract C { + constructor() { + uint256 x; + assembly { x := 0 } + f(); + } + function f() internal pure { + assembly { mstore(0, 0) } + } + function g() public pure { + /// @solidity memory-safe-assembly + assembly { mstore(0, 0) } + } +} +// ---- +// :C(creation) false +// :C(runtime) true diff --git a/test/libsolidity/memoryGuardTests/free_function.sol b/test/libsolidity/memoryGuardTests/free_function.sol new file mode 100644 index 000000000..a417189ed --- /dev/null +++ b/test/libsolidity/memoryGuardTests/free_function.sol @@ -0,0 +1,29 @@ +function safe() pure returns (uint256 x) { + assembly { x := 42 } + /// @solidity memory-safe-assembly + assembly { mstore(0, 0) } +} +function unsafe() pure returns (uint256 x) { + assembly { pop(mload(0)) } +} +contract C { + constructor() { + unsafe(); + } + function f() public pure { + safe(); + } +} +contract D { + constructor() { + safe(); + } + function f() public pure { + unsafe(); + } +} +// ---- +// :C(creation) false +// :C(runtime) true +// :D(creation) true +// :D(runtime) false diff --git a/test/libsolidity/memoryGuardTests/multi_annotation.sol b/test/libsolidity/memoryGuardTests/multi_annotation.sol new file mode 100644 index 000000000..478546b83 --- /dev/null +++ b/test/libsolidity/memoryGuardTests/multi_annotation.sol @@ -0,0 +1,17 @@ +contract C { + constructor() { + /// @solidity memory-safe-assembly a memory-safe-assembly + assembly { mstore(0, 0) } + } + function f() internal pure { + /// @solidity a memory-safe-assembly + assembly { mstore(0, 0) } + /// @solidity a + /// memory-safe-assembly + /// b + assembly { mstore(0, 0) } + } +} +// ---- +// :C(creation) true +// :C(runtime) true diff --git a/test/libsolidity/memoryGuardTests/multiple_contracts.sol b/test/libsolidity/memoryGuardTests/multiple_contracts.sol new file mode 100644 index 000000000..84d2cb453 --- /dev/null +++ b/test/libsolidity/memoryGuardTests/multiple_contracts.sol @@ -0,0 +1,25 @@ +contract C { + constructor(uint256 x) { + assembly { x := 4 } + /// @solidity memory-safe-assembly + assembly { mstore(0, 0) } + } + function f() public pure { + assembly { mstore(0,0) } + } +} +contract D { + constructor() { + assembly { mstore(0,0) } + } + function f(uint256 x) public pure { + assembly { x := 4 } + /// @solidity memory-safe-assembly + assembly { mstore(0, 0) } + } +} +// ---- +// :C(creation) true +// :C(runtime) false +// :D(creation) false +// :D(runtime) true diff --git a/test/libsolidity/memoryGuardTests/safe_and_unmarked_empty.sol b/test/libsolidity/memoryGuardTests/safe_and_unmarked_empty.sol new file mode 100644 index 000000000..614201deb --- /dev/null +++ b/test/libsolidity/memoryGuardTests/safe_and_unmarked_empty.sol @@ -0,0 +1,10 @@ +contract C { + function f() external pure { + /// @solidity memory-safe-assembly + assembly {} + assembly {} + } +} +// ---- +// :C(creation) true +// :C(runtime) true diff --git a/test/libsolidity/memoryGuardTests/safe_and_unmarked_unsafe.sol b/test/libsolidity/memoryGuardTests/safe_and_unmarked_unsafe.sol new file mode 100644 index 000000000..32d19993c --- /dev/null +++ b/test/libsolidity/memoryGuardTests/safe_and_unmarked_unsafe.sol @@ -0,0 +1,10 @@ +contract C { + function f() external pure { + /// @solidity memory-safe-assembly + assembly {} + assembly { mstore(0,0) } + } +} +// ---- +// :C(creation) true +// :C(runtime) false diff --git a/test/libsolidity/memoryGuardTests/stub.sol b/test/libsolidity/memoryGuardTests/stub.sol new file mode 100644 index 000000000..65f6447c1 --- /dev/null +++ b/test/libsolidity/memoryGuardTests/stub.sol @@ -0,0 +1,4 @@ +contract C {} +// ---- +// :C(creation) true +// :C(runtime) true diff --git a/test/libsolidity/memoryGuardTests/unmarked_but_no_memory_access.sol b/test/libsolidity/memoryGuardTests/unmarked_but_no_memory_access.sol new file mode 100644 index 000000000..8af88d75d --- /dev/null +++ b/test/libsolidity/memoryGuardTests/unmarked_but_no_memory_access.sol @@ -0,0 +1,8 @@ +contract C { + function f(uint256 x, uint256 y) public pure returns (uint256 z){ + assembly { z := add(x, y) } + } +} +// ---- +// :C(creation) true +// :C(runtime) true diff --git a/test/libsolidity/memoryGuardTests/unmarked_with_memory_access.sol b/test/libsolidity/memoryGuardTests/unmarked_with_memory_access.sol new file mode 100644 index 000000000..88f826fdb --- /dev/null +++ b/test/libsolidity/memoryGuardTests/unmarked_with_memory_access.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + assembly { mstore(0,0) } + } +} +// ---- +// :C(creation) true +// :C(runtime) false diff --git a/test/libsolidity/memoryGuardTests/unmarked_with_memory_assignment.sol b/test/libsolidity/memoryGuardTests/unmarked_with_memory_assignment.sol new file mode 100644 index 000000000..c7519a65e --- /dev/null +++ b/test/libsolidity/memoryGuardTests/unmarked_with_memory_assignment.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + bytes memory x; + assembly { + x := 0 + } + } +} +// ---- +// :C(creation) true +// :C(runtime) false diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol index 720f57489..0488179f1 100644 --- a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol @@ -50,7 +50,7 @@ contract test { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 1790188 +// gas irOptimized: 1792108 // gas legacy: 2250130 // gas legacyOptimized: 1746528 // div(uint256,uint256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid_natspec.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid_natspec.sol index 3a6ccefb3..d23eceeed 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid_natspec.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid_natspec.sol @@ -9,6 +9,6 @@ contract C { } } // ---- -// Warning 6269: (60-71): Unexpected natspec tag in inline assembly: test +// Warning 6269: (60-71): Unexpected NatSpec tag "test" with value "test" in inline assembly. // Warning 8787: (95-106): Unexpected value for @solidity tag in inline assembly: test -// Warning 7828: (122-133): Inline assembly has invalid natspec documentation. +// Warning 7828: (122-133): Inline assembly has invalid NatSpec documentation. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/natspec_multi.sol b/test/libsolidity/syntaxTests/inlineAssembly/natspec_multi.sol new file mode 100644 index 000000000..945d62b06 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/natspec_multi.sol @@ -0,0 +1,23 @@ +function f() pure { + /// @unrelated bogus-value + + /// @before bogus-value + /// + /// @solidity a memory-safe-assembly b c + /// d + /// @after bogus-value + assembly {} + /// @solidity memory-safe-assembly a a a + /// memory-safe-assembly + assembly {} +} +// ---- +// Warning 6269: (189-200): Unexpected NatSpec tag "after" with value "bogus-value" in inline assembly. +// Warning 6269: (189-200): Unexpected NatSpec tag "before" with value "bogus-value" in inline assembly. +// Warning 8787: (189-200): Unexpected value for @solidity tag in inline assembly: a +// Warning 8787: (189-200): Unexpected value for @solidity tag in inline assembly: b +// Warning 8787: (189-200): Unexpected value for @solidity tag in inline assembly: c +// Warning 8787: (189-200): Unexpected value for @solidity tag in inline assembly: d +// Warning 8787: (289-300): Unexpected value for @solidity tag in inline assembly: a +// Warning 4377: (289-300): Value for @solidity tag in inline assembly specified multiple times: a +// Warning 4377: (289-300): Value for @solidity tag in inline assembly specified multiple times: memory-safe-assembly diff --git a/test/libsolidity/syntaxTests/inlineAssembly/natspec_multi_swallowed.sol b/test/libsolidity/syntaxTests/inlineAssembly/natspec_multi_swallowed.sol new file mode 100644 index 000000000..74be1232d --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/natspec_multi_swallowed.sol @@ -0,0 +1,19 @@ +function f() pure { + /// @unrelated bogus-value + + /// @before + /// + /// @solidity a memory-safe-assembly b c + /// d + /// @after bogus-value + assembly {} + /// @solidity memory-safe-assembly a a a + /// memory-safe-assembly + assembly {} +} +// ---- +// Warning 6269: (177-188): Unexpected NatSpec tag "after" with value "bogus-value" in inline assembly. +// Warning 6269: (177-188): Unexpected NatSpec tag "before" with value "@solidity a memory-safe-assembly b c d" in inline assembly. +// Warning 8787: (277-288): Unexpected value for @solidity tag in inline assembly: a +// Warning 4377: (277-288): Value for @solidity tag in inline assembly specified multiple times: a +// Warning 4377: (277-288): Value for @solidity tag in inline assembly specified multiple times: memory-safe-assembly diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 18e3d4377..5971341ec 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -23,6 +23,7 @@ add_executable(isoltest ../libsolidity/util/TestFileParser.cpp ../libsolidity/util/TestFunctionCall.cpp ../libsolidity/GasTest.cpp + ../libsolidity/MemoryGuardTest.cpp ../libsolidity/SyntaxTest.cpp ../libsolidity/SemanticTest.cpp ../libsolidity/AnalysisFramework.cpp