Merge pull request #10783 from ethereum/circular-constant-variable

Fix infinite loop when accessing circular constants from inline assem…
This commit is contained in:
chriseth 2021-01-26 09:24:24 +01:00 committed by GitHub
commit de0a3b989d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 85 additions and 0 deletions

View File

@ -32,6 +32,7 @@ Bugfixes:
* SMTChecker: Fix internal error on pushing string literal to ``bytes`` array.
* Type Checker: Fix internal error caused by constant structs containing mappings.
* Type System: Disallow implicit conversion from ``uintN`` to ``intM`` when ``M > N``, and by extension, explicit conversion between the same types is also disallowed.
* Type Checker: Fix infinite loop when accessing circular constants from inline assembly.
* Control Flow Graph: Fix missing error caused by read from/write to uninitialized variables.
### 0.8.0 (2020-12-16)

View File

@ -753,6 +753,16 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
}
if (var->isConstant())
{
if (isConstantVariableRecursive(*var))
{
m_errorReporter.typeError(
3558_error,
_identifier.location,
"Constant variable is circular."
);
return false;
}
var = rootConstVariableDeclaration(*var);
if (var && !var->value())

View File

@ -19,12 +19,41 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTUtils.h>
#include <libsolutil/Algorithms.h>
namespace solidity::frontend
{
bool isConstantVariableRecursive(VariableDeclaration const& _varDecl)
{
solAssert(_varDecl.isConstant(), "Constant variable expected");
auto referencedDeclaration = [&](Expression const* _e) -> VariableDeclaration const*
{
if (auto identifier = dynamic_cast<Identifier const*>(_e))
return dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration);
else if (auto memberAccess = dynamic_cast<MemberAccess const*>(_e))
return dynamic_cast<VariableDeclaration const*>(memberAccess->annotation().referencedDeclaration);
return nullptr;
};
auto visitor = [&](VariableDeclaration const& _variable, util::CycleDetector<VariableDeclaration>& _cycleDetector, size_t _depth)
{
solAssert(_depth < 256, "Recursion depth limit reached");
if (auto referencedVarDecl = referencedDeclaration(_variable.value().get()))
if (referencedVarDecl->isConstant())
if (_cycleDetector.run(*referencedVarDecl))
return;
};
return util::CycleDetector<VariableDeclaration>(visitor).run(_varDecl);
}
VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration const& _varDecl)
{
solAssert(_varDecl.isConstant(), "Constant variable expected");
solAssert(!isConstantVariableRecursive(_varDecl), "Recursive declaration");
VariableDeclaration const* rootDecl = &_varDecl;
Identifier const* identifier;

View File

@ -28,4 +28,7 @@ class VariableDeclaration;
/// Returns nullptr if an identifier in the chain is not referencing a constant variable declaration.
VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration const& _varDecl);
/// Returns true if the constant variable declaration is recursive.
bool isConstantVariableRecursive(VariableDeclaration const& _varDecl);
}

View File

@ -0,0 +1,11 @@
contract C {
bytes32 constant x = x;
function f() public pure returns (uint t) {
assembly {
// Reference to a circular member
t := x
}
}
}
// ----
// TypeError 3558: (171-172): Constant variable is circular.

View File

@ -0,0 +1,17 @@
==== Source: a ====
import "b";
uint constant c = d;
==== Source: b ====
import "a" as M;
uint constant b = M.c;
uint constant d = b;
contract C {
uint constant a = b;
function f() public returns (uint t) {
assembly {
t := a
}
}
}
// ----
// TypeError 3558: (b:178-179): Constant variable is circular.

View File

@ -0,0 +1,14 @@
==== Source: a ====
bytes32 constant x = x;
==== Source: b ====
import "a";
contract C {
function f() public pure returns (uint t) {
assembly {
// Reference to a circular member
t := x
}
}
}
// ----
// TypeError 3558: (b:155-156): Constant variable is circular.