Analyze inline assembly variable declarations for invalid or shadowing names.

This commit is contained in:
Daniel Kirchner 2019-11-06 18:06:36 +01:00
parent 30ea41c36d
commit 0556f64722
14 changed files with 154 additions and 3 deletions

View File

@ -13,6 +13,7 @@ Breaking changes:
* Syntax: ``push(element)`` for dynamic storage arrays do not return the new length anymore. * Syntax: ``push(element)`` for dynamic storage arrays do not return the new length anymore.
* Syntax: Abstract contracts need to be marked explicitly as abstract by using the ``abstract`` keyword. * Syntax: Abstract contracts need to be marked explicitly as abstract by using the ``abstract`` keyword.
* Inline Assembly: Only strict inline assembly is allowed. * Inline Assembly: Only strict inline assembly is allowed.
* Inline Assembly: Variable declarations cannot shadow declarations outside the assembly block.
* Type checker: Resulting type of exponentiation is equal to the type of the base. Also allow signed types for the base. * Type checker: Resulting type of exponentiation is equal to the type of the base. Also allow signed types for the base.
* Source mappings: Add "modifier depth" as a fifth field in the source mappings. * Source mappings: Add "modifier depth" as a fifth field in the source mappings.

View File

@ -279,10 +279,34 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
ErrorList errors; ErrorList errors;
ErrorReporter errorsIgnored(errors); ErrorReporter errorsIgnored(errors);
yul::ExternalIdentifierAccess::Resolver resolver = yul::ExternalIdentifierAccess::Resolver resolver =
[&](yul::Identifier const& _identifier, yul::IdentifierContext, bool _crossesFunctionBoundary) { [&](yul::Identifier const& _identifier, yul::IdentifierContext _context, bool _crossesFunctionBoundary) {
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str());
bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), "_slot"); bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), "_slot");
bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), "_offset"); bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), "_offset");
if (_context == yul::IdentifierContext::VariableDeclaration)
{
string namePrefix = _identifier.name.str().substr(0, _identifier.name.str().find('.'));
if (isSlot || isOffset)
declarationError(_identifier.location, "In variable declarations _slot and _offset can not be used as a suffix.");
else if (
auto declarations = m_resolver.nameFromCurrentScope(namePrefix);
!declarations.empty()
)
{
SecondarySourceLocation ssl;
for (auto const* decl: declarations)
ssl.append("The shadowed declaration is here:", decl->location());
if (!ssl.infos.empty())
declarationError(
_identifier.location,
ssl,
namePrefix.size() < _identifier.name.str().size() ?
"The prefix of this declaration conflicts with a declaration outside the inline assembly block." :
"This declaration shadows a declaration outside the inline assembly block."
);
}
return size_t(-1);
}
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str());
if (isSlot || isOffset) if (isSlot || isOffset)
{ {
// special mode to access storage variables // special mode to access storage variables
@ -464,6 +488,12 @@ void ReferencesResolver::declarationError(SourceLocation const& _location, strin
m_errorReporter.declarationError(_location, _description); m_errorReporter.declarationError(_location, _description);
} }
void ReferencesResolver::declarationError(SourceLocation const& _location, SecondarySourceLocation const& _ssl, string const& _description)
{
m_errorOccurred = true;
m_errorReporter.declarationError(_location, _ssl, _description);
}
void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, string const& _description) void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, string const& _description)
{ {
m_errorOccurred = true; m_errorOccurred = true;

View File

@ -91,6 +91,9 @@ private:
/// Adds a new error to the list of errors. /// Adds a new error to the list of errors.
void declarationError(langutil::SourceLocation const& _location, std::string const& _description); void declarationError(langutil::SourceLocation const& _location, std::string const& _description);
/// Adds a new error to the list of errors.
void declarationError(langutil::SourceLocation const& _location, langutil::SecondarySourceLocation const& _ssl, std::string const& _description);
/// Adds a new error to the list of errors and throws to abort reference resolving. /// Adds a new error to the list of errors and throws to abort reference resolving.
void fatalDeclarationError(langutil::SourceLocation const& _location, std::string const& _description); void fatalDeclarationError(langutil::SourceLocation const& _location, std::string const& _description);

View File

@ -244,6 +244,14 @@ bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl)
{ {
bool success = true; bool success = true;
int const numVariables = _varDecl.variables.size(); int const numVariables = _varDecl.variables.size();
if (m_resolver)
for (auto const& variable: _varDecl.variables)
// Call the resolver for variable declarations to allow it to raise errors on shadowing.
m_resolver(
yul::Identifier{variable.location, variable.name},
yul::IdentifierContext::VariableDeclaration,
m_currentScope->insideFunction()
);
if (_varDecl.value) if (_varDecl.value)
{ {
int const stackHeight = m_stackHeight; int const stackHeight = m_stackHeight;

View File

@ -111,7 +111,7 @@ public:
virtual SubID appendData(dev::bytes const& _data) = 0; virtual SubID appendData(dev::bytes const& _data) = 0;
}; };
enum class IdentifierContext { LValue, RValue }; enum class IdentifierContext { LValue, RValue, VariableDeclaration };
/// Object that is used to resolve references and generate code for access to identifiers external /// Object that is used to resolve references and generate code for access to identifiers external
/// to inline assembly (not used in standalone assembly mode). /// to inline assembly (not used in standalone assembly mode).

View File

@ -0,0 +1,15 @@
contract C {
function f() public pure {
assembly {
let x_offset := 1
let x_slot := 1
let _offset := 1
let _slot := 1
}
}
}
// ----
// DeclarationError: (79-87): In variable declarations _slot and _offset can not be used as a suffix.
// DeclarationError: (109-115): In variable declarations _slot and _offset can not be used as a suffix.
// DeclarationError: (137-144): In variable declarations _slot and _offset can not be used as a suffix.
// DeclarationError: (166-171): In variable declarations _slot and _offset can not be used as a suffix.

View File

@ -0,0 +1,9 @@
contract C {
function f(uint a) public pure {
assembly {
let a := 1
}
}
}
// ----
// DeclarationError: (85-86): This declaration shadows a declaration outside the inline assembly block.

View File

@ -0,0 +1,10 @@
contract C {
uint constant a;
function f() public pure {
assembly {
let a := 1
}
}
}
// ----
// DeclarationError: (100-101): This declaration shadows a declaration outside the inline assembly block.

View File

@ -0,0 +1,9 @@
contract C {
function f() public pure {
assembly {
let C := 1
}
}
}
// ----
// DeclarationError: (79-80): This declaration shadows a declaration outside the inline assembly block.

View File

@ -0,0 +1,9 @@
contract C {
function f() public pure {
assembly {
let f := 1
}
}
}
// ----
// DeclarationError: (79-80): This declaration shadows a declaration outside the inline assembly block.

View File

@ -0,0 +1,10 @@
contract C {
function f() public pure {
uint a;
assembly {
let a := 1
}
}
}
// ----
// DeclarationError: (95-96): This declaration shadows a declaration outside the inline assembly block.

View File

@ -0,0 +1,18 @@
==== Source: a ====
contract A
{
uint constant a = 42;
}
==== Source: b ====
import {A as b} from "a";
contract B {
function f() public pure {
assembly {
let b := 3
let b.a := 4
}
}
}
// ----
// DeclarationError: (b:105-106): This declaration shadows a declaration outside the inline assembly block.
// DeclarationError: (b:128-131): The prefix of this declaration conflicts with a declaration outside the inline assembly block.

View File

@ -0,0 +1,15 @@
==== Source: a ====
contract A
{
uint constant a = 42;
}
==== Source: b ====
import {A as b} from "a";
contract B {
function f() public pure {
assembly {
let A := 1
let A.b := 2
}
}
}

View File

@ -0,0 +1,14 @@
contract D {
uint constant a;
}
contract C {
function f() public pure {
assembly {
let D.a := 1
let D.b := 1 // shadowing the prefix only is also an error
}
}
}
// ----
// DeclarationError: (115-118): The prefix of this declaration conflicts with a declaration outside the inline assembly block.
// DeclarationError: (140-143): The prefix of this declaration conflicts with a declaration outside the inline assembly block.