Allow annotating inline assembly as memory-safe.

This commit is contained in:
Daniel Kirchner 2022-02-03 11:49:16 +01:00
parent e8520a667b
commit e6848caac1
9 changed files with 100 additions and 9 deletions

View File

@ -1,6 +1,7 @@
### 0.8.13 (unreleased) ### 0.8.13 (unreleased)
Language Features: 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: Compiler Features:

View File

@ -30,6 +30,7 @@
#include <liblangutil/Common.h> #include <liblangutil/Common.h>
#include <range/v3/algorithm/any_of.hpp> #include <range/v3/algorithm/any_of.hpp>
#include <range/v3/view/filter.hpp>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
@ -162,6 +163,71 @@ bool DocStringTagParser::visit(ErrorDefinition const& _error)
return true; 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<string> values;
boost::split(values, tagValue.content, isWhiteSpace);
set<string> valuesSeen;
set<string> 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( void DocStringTagParser::checkParameters(
CallableDeclaration const& _callable, CallableDeclaration const& _callable,
StructurallyDocumented const& _node, StructurallyDocumented const& _node,

View File

@ -48,6 +48,7 @@ private:
bool visit(ModifierDefinition const& _modifier) override; bool visit(ModifierDefinition const& _modifier) override;
bool visit(EventDefinition const& _event) override; bool visit(EventDefinition const& _event) override;
bool visit(ErrorDefinition const& _error) override; bool visit(ErrorDefinition const& _error) override;
bool visit(InlineAssembly const& _assembly) override;
void checkParameters( void checkParameters(
CallableDeclaration const& _callable, CallableDeclaration const& _callable,

View File

@ -220,6 +220,8 @@ struct InlineAssemblyAnnotation: StatementAnnotation
std::map<yul::Identifier const*, ExternalIdentifierInfo> externalReferences; std::map<yul::Identifier const*, ExternalIdentifierInfo> externalReferences;
/// Information generated during analysis phase. /// Information generated during analysis phase.
std::shared_ptr<yul::AsmAnalysisInfo> analysisInfo; std::shared_ptr<yul::AsmAnalysisInfo> analysisInfo;
/// True, if the assembly block was annotated to be memory-safe.
bool memorySafe = false;
}; };
struct BlockAnnotation: StatementAnnotation, ScopableAnnotation struct BlockAnnotation: StatementAnnotation, ScopableAnnotation

View File

@ -160,8 +160,8 @@ public:
std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; } std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }
bool inlineAssemblySeen() const { return m_inlineAssemblySeen; } bool memoryUnsafeInlineAssemblySeen() const { return m_memoryUnsafeInlineAssemblySeen; }
void setInlineAssemblySeen() { m_inlineAssemblySeen = true; } void setMemoryUnsafeInlineAssemblySeen() { m_memoryUnsafeInlineAssemblySeen = true; }
/// @returns the runtime ID to be used for the function in the dispatch routine /// @returns the runtime ID to be used for the function in the dispatch routine
/// and for internal function pointers. /// and for internal function pointers.
@ -202,8 +202,8 @@ private:
/// Whether to use checked or wrapping arithmetic. /// Whether to use checked or wrapping arithmetic.
Arithmetic m_arithmetic = Arithmetic::Checked; Arithmetic m_arithmetic = Arithmetic::Checked;
/// Flag indicating whether any inline assembly block was seen. /// Flag indicating whether any memory-unsafe inline assembly block was seen.
bool m_inlineAssemblySeen = false; bool m_memoryUnsafeInlineAssemblySeen = false;
/// Function definitions queued for code generation. They're the Solidity functions whose calls /// Function definitions queued for code generation. They're the Solidity functions whose calls
/// were discovered by the IR generator during AST traversal. /// were discovered by the IR generator during AST traversal.

View File

@ -213,8 +213,8 @@ string IRGenerator::generate(
t("subObjects", subObjectSources(m_context.subObjectsCreated())); t("subObjects", subObjectSources(m_context.subObjectsCreated()));
// This has to be called only after all other code generation for the creation object is complete. // This has to be called only after all other code generation for the creation object is complete.
bool creationInvolvesAssembly = m_context.inlineAssemblySeen(); bool creationInvolvesMemoryUnsafeAssembly = m_context.memoryUnsafeInlineAssemblySeen();
t("memoryInitCreation", memoryInit(!creationInvolvesAssembly)); t("memoryInitCreation", memoryInit(!creationInvolvesMemoryUnsafeAssembly));
t("useSrcMapCreation", formatUseSrcMap(m_context)); t("useSrcMapCreation", formatUseSrcMap(m_context));
resetContext(_contract, ExecutionContext::Deployed); resetContext(_contract, ExecutionContext::Deployed);
@ -239,8 +239,8 @@ string IRGenerator::generate(
t("useSrcMapDeployed", formatUseSrcMap(m_context)); t("useSrcMapDeployed", formatUseSrcMap(m_context));
// This has to be called only after all other code generation for the deployed object is complete. // This has to be called only after all other code generation for the deployed object is complete.
bool deployedInvolvesAssembly = m_context.inlineAssemblySeen(); bool deployedInvolvesMemoryUnsafeAssembly = m_context.memoryUnsafeInlineAssemblySeen();
t("memoryInitDeployed", memoryInit(!deployedInvolvesAssembly)); t("memoryInitDeployed", memoryInit(!deployedInvolvesMemoryUnsafeAssembly));
solAssert(_contract.annotation().creationCallGraph->get() != nullptr, ""); solAssert(_contract.annotation().creationCallGraph->get() != nullptr, "");
solAssert(_contract.annotation().deployedCallGraph->get() != nullptr, ""); solAssert(_contract.annotation().deployedCallGraph->get() != nullptr, "");

View File

@ -2138,7 +2138,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
{ {
setLocation(_inlineAsm); setLocation(_inlineAsm);
m_context.setInlineAssemblySeen(); if (!_inlineAsm.annotation().memorySafe)
m_context.setMemoryUnsafeInlineAssemblySeen();
CopyTranslate bodyCopier{_inlineAsm.dialect(), m_context, _inlineAsm.annotation().externalReferences}; CopyTranslate bodyCopier{_inlineAsm.dialect(), m_context, _inlineAsm.annotation().externalReferences};
yul::Statement modified = bodyCopier(_inlineAsm.operations()); yul::Statement modified = bodyCopier(_inlineAsm.operations());

View File

@ -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.

View File

@ -0,0 +1,6 @@
contract C {
function f() public pure {
// @solidity memory-safe-assembly
assembly {}
}
}