Merge pull request #8567 from ethereum/storage-pointer-checker

Removed redundant storage declaration check; test coverages
This commit is contained in:
chriseth 2020-04-02 13:46:51 +02:00 committed by GitHub
commit a2b427dc0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 662 additions and 16 deletions

View File

@ -1063,17 +1063,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
if (!varDecl.annotation().type)
m_errorReporter.fatalTypeError(_statement.location(), "Use of the \"var\" keyword is disallowed.");
if (auto ref = dynamic_cast<ReferenceType const*>(type(varDecl)))
{
if (ref->dataStoredIn(DataLocation::Storage))
{
string errorText{"Uninitialized storage pointer."};
solAssert(varDecl.referenceLocation() != VariableDeclaration::Location::Unspecified, "Expected a specified location at this point");
solAssert(m_scope, "");
m_errorReporter.declarationError(varDecl.location(), errorText);
}
}
else if (dynamic_cast<MappingType const*>(type(varDecl)))
if (dynamic_cast<MappingType const*>(type(varDecl)))
m_errorReporter.typeError(
varDecl.location(),
"Uninitialized mapping. Mappings cannot be created dynamically, you have to assign them from a state variable."

View File

@ -2,8 +2,10 @@ contract C {
function f() public {
uint[] storage x;
uint[10] storage y;
x;
y;
}
}
// ----
// DeclarationError: (38-54): Uninitialized storage pointer.
// DeclarationError: (58-76): Uninitialized storage pointer.
// TypeError: (80-81): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (85-86): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -0,0 +1,38 @@
contract C {
struct S { bool f; }
S s;
function f() internal pure {
S storage c;
assembly {
for {} eq(0,0) { c_slot := s_slot } {}
}
c;
}
function g() internal pure {
S storage c;
assembly {
for {} eq(0,1) { c_slot := s_slot } {}
}
c;
}
function h() internal pure {
S storage c;
assembly {
for {} eq(0,0) {} { c_slot := s_slot }
}
c;
}
function i() internal pure {
S storage c;
assembly {
for {} eq(0,1) {} { c_slot := s_slot }
}
c;
}
}
// ----
// TypeError: (189-190): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (340-341): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (491-492): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (642-643): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -0,0 +1,19 @@
contract C {
struct S { bool f; }
S s;
function f() internal pure {
S storage c;
assembly {
for { c_slot := s_slot } iszero(0) {} {}
}
c;
}
function g() internal pure {
S storage c;
assembly {
for { c_slot := s_slot } iszero(1) {} {}
}
c;
}
}
// ----

View File

@ -0,0 +1,13 @@
contract C {
struct S { bool f; }
S s;
function f(bool flag) internal pure {
S storage c;
assembly {
if flag { c_slot := s_slot }
}
c;
}
}
// ----
// TypeError: (188-189): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -0,0 +1,15 @@
contract C {
struct S { bool f; }
S s;
function f() internal pure {
S storage c;
// this should warn about unreachable code, but currently function flow is ignored
assembly {
function f() { return(0, 0) }
f()
c_slot := s_slot
}
c;
}
}
// ----

View File

@ -0,0 +1,15 @@
contract C {
struct S { bool f; }
S s;
function f() internal pure {
S storage c;
// this could be allowed, but currently control flow for functions is not analysed
assembly {
function f() { revert(0, 0) }
f()
}
c;
}
}
// ----
// TypeError: (287-288): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -0,0 +1,12 @@
contract C {
struct S { bool f; }
S s;
function f() internal pure {
S storage c;
assembly {
c_slot := s_slot
}
c;
}
}
// ----

View File

@ -0,0 +1,33 @@
contract C {
struct S { bool f; }
S s;
function f(uint256 a) internal pure {
S storage c;
assembly {
switch a
case 0 { c_slot := s_slot }
}
c;
}
function g(bool flag) internal pure {
S storage c;
assembly {
switch flag
case 0 { c_slot := s_slot }
case 1 { c_slot := s_slot }
}
c;
}
function h(uint256 a) internal pure {
S storage c;
assembly {
switch a
case 0 { c_slot := s_slot }
default { return(0,0) }
}
c;
}
}
// ----
// TypeError: (208-209): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (421-422): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -0,0 +1,31 @@
contract C {
struct S { bool f; }
S s;
function f(uint256 a) internal pure {
S storage c;
assembly {
switch a
default { c_slot := s_slot }
}
c;
}
function g(bool flag) internal pure {
S storage c;
assembly {
switch flag
case 0 { c_slot := s_slot }
default { c_slot := s_slot }
}
c;
}
function h(uint256 a) internal pure {
S storage c;
assembly {
switch a
case 0 { revert(0, 0) }
default { c_slot := s_slot }
}
c;
}
}
// ----

View File

@ -0,0 +1,66 @@
contract C {
struct S { bool f; }
S s;
function f() internal view {
S storage c;
do {
break;
c = s;
} while(false);
c;
}
function g() internal view {
S storage c;
do {
if (s.f) {
continue;
c = s;
}
else {
}
} while(false);
c;
}
function h() internal view {
S storage c;
do {
if (s.f) {
break;
}
else {
c = s;
}
} while(false);
c;
}
function i() internal view {
S storage c;
do {
if (s.f) {
continue;
}
else {
c = s;
}
} while(false);
c;
}
function j() internal view {
S storage c;
do {
continue;
c = s;
} while(false);
c;
}
}
// ----
// TypeError: (184-185): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// Warning: (145-150): Unreachable code.
// Warning: (168-173): Unreachable code.
// TypeError: (411-412): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// Warning: (325-330): Unreachable code.
// TypeError: (635-636): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (862-863): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (1011-1012): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// Warning: (972-977): Unreachable code.

View File

@ -0,0 +1,44 @@
contract C {
struct S { bool f; }
S s;
function f() internal view {
S storage c;
do {} while((c = s).f);
c;
}
function g() internal view {
S storage c;
do { c = s; } while(false);
c;
}
function h() internal view {
S storage c;
c = s;
do {} while(false);
c;
}
function i() internal view {
S storage c;
do {} while(false);
c = s;
c;
}
function j() internal view {
S storage c;
do {
c = s;
break;
} while(false);
c;
}
function k() internal view {
S storage c;
do {
c = s;
continue;
} while(false);
c;
}
}
// ----
// Warning: (606-611): Unreachable code.

View File

@ -0,0 +1,20 @@
contract C {
struct S { bool f; }
S s;
function f() internal view {
S storage c;
for(;; c = s) {
}
c;
}
function g() internal view {
S storage c;
for(;;) {
c = s;
}
c;
}
}
// ----
//TypeError: (143-144): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (261-262): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -0,0 +1,17 @@
contract C {
struct S { bool f; }
S s;
function f() internal view {
S storage c;
for(c = s;;) {
}
c;
}
function g() internal view {
S storage c;
for(; (c = s).f;) {
}
c;
}
}
// ----

View File

@ -0,0 +1,22 @@
contract C {
struct S { bool f; }
S s;
function f(bool flag) internal {
S storage c;
if (flag) c = s;
c;
}
function g(bool flag) internal {
S storage c;
if (flag) c = s;
else
{
if (!flag) c = s;
else s.f = true;
}
c;
}
}
// ----
// TypeError: (138-139): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (330-331): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -0,0 +1,39 @@
contract C {
struct S { bool f; }
S s;
function f(bool flag) internal view {
S storage c;
if (flag) c = s;
else c = s;
c;
}
function g(bool flag) internal view {
S storage c;
if (flag) c = s;
else { c = s; }
c;
}
function h(bool flag) internal view {
S storage c;
if (flag) c = s;
else
{
if (!flag) c = s;
else c = s;
}
c;
}
function i() internal view {
S storage c;
if ((c = s).f) {
}
c;
}
function j() internal view {
S storage c;
if ((c = s).f && !(c = s).f) {
}
c;
}
}
// ----

View File

@ -0,0 +1,20 @@
contract C {
modifier revertIfNoReturn() {
_;
revert();
}
modifier ifFlag(bool flag) {
if (flag)
_;
}
struct S { uint a; }
S s;
function f(bool flag) revertIfNoReturn() internal view {
if (flag) s;
}
function g(bool flag) revertIfNoReturn() ifFlag(flag) internal view {
s;
}
}
// ----

View File

@ -0,0 +1,11 @@
contract C {
struct S { bool f; }
S s;
function g(bool flag) internal view {
S storage c;
if (flag) c = s;
else revert();
s;
}
}
// ----

View File

@ -0,0 +1,24 @@
contract C {
struct S { bool f; }
S s;
function f() internal view {
S storage c;
false && (c = s).f;
c;
}
function g() internal view {
S storage c;
true || (c = s).f;
c;
}
function h() internal view {
S storage c;
// expect error, although this is always fine
true && (false || (c = s).f);
c;
}
}
// ----
// TypeError: (137-138): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (235-236): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (398-399): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -0,0 +1,15 @@
contract C {
struct S { bool f; }
S s;
function f() internal view {
S storage c;
(c = s).f && false;
c;
}
function g() internal view {
S storage c;
(c = s).f || true;
c;
}
}
// ----

View File

@ -0,0 +1,17 @@
contract C {
struct S { bool f; }
S s;
function f() internal pure {}
function g() internal view { s; }
function h() internal view {
S storage c;
c = s;
c;
}
function i() internal view {
S storage c;
(c) = s;
c;
}
}
// ----

View File

@ -0,0 +1,11 @@
contract C {
uint256[] s;
function f() public {
bool d;
uint256[] storage x;
uint256[] storage y = d ? (x = s) : x;
y;
}
}
// ----
// TypeError: (145-146): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -0,0 +1,9 @@
contract C {
uint256[] s;
function f() public view {
uint256[] storage x;
uint256[] storage y = (x = s)[0] > 0 ? x : x;
y;
}
}
// ---

View File

@ -0,0 +1,17 @@
contract C {
struct S { bool f; }
S s;
function f(bool flag) internal view {
S storage c;
flag ? (c = s).f : false;
c;
}
function g(bool flag) internal view {
S storage c;
flag ? false : (c = s).f;
c;
}
}
// ----
// TypeError: (152-153): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (266-267): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -0,0 +1,20 @@
contract C {
struct S { bool f; }
S s;
function f(bool flag) internal view {
S storage c;
flag ? c = s : c = s;
c;
}
function g(bool flag) internal view {
S storage c;
flag ? c = s : (c = s);
c;
}
function h(bool flag) internal view {
S storage c;
flag ? (c = s).f : (c = s).f;
c;
}
}
// ----

View File

@ -0,0 +1,42 @@
contract C {
struct S { bool f; }
S s;
function ext() external {}
function f() internal
{
S storage r;
try this.ext() { }
catch (bytes memory) { r = s; }
r;
}
function g() internal
{
S storage r;
try this.ext() { r = s; }
catch (bytes memory) { }
r;
}
function h() internal
{
S storage r;
try this.ext() {}
catch Error (string memory) { r = s; }
catch (bytes memory) { r = s; }
r;
}
function i() internal
{
S storage r;
try this.ext() { r = s; }
catch (bytes memory) { r; }
r = s;
r;
}
}
// ====
// EVMVersion: >=byzantium
// ----
// TypeError: (206-207): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (343-344): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (526-527): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.
// TypeError: (653-654): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -0,0 +1,30 @@
contract C {
struct S { bool f; }
S s;
function ext() external { }
function f() internal
{
S storage r;
try this.ext() { r = s; }
catch (bytes memory) { r = s; }
r;
}
function g() internal
{
S storage r;
try this.ext() { r = s; }
catch Error (string memory) { r = s; }
catch (bytes memory) { r = s; }
r;
}
function h() internal
{
S storage r;
try this.ext() { }
catch (bytes memory) { }
r = s;
r;
}
}
// ====
// EVMVersion: >=byzantium

View File

@ -0,0 +1,17 @@
contract C {
struct S { bool f; }
S s;
function f() internal view returns (S storage, uint) {
return (s,2);
}
function g() internal view {
uint a;
S storage c;
(c, a) = f();
c;
}
function h() internal view {
(s, s);
}
}
// ----

View File

@ -0,0 +1,13 @@
contract C {
struct S { bool f; }
S s;
function f() internal view {
S storage c;
while(false) {
c = s;
}
c;
}
}
// ----
// TypeError: (161-162): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -0,0 +1,25 @@
contract C {
struct S { bool f; }
S s;
function f() internal view {
S storage c;
while((c = s).f) {
}
c;
}
function g() internal view {
S storage c;
c = s;
while(false) {
}
c;
}
function h() internal view {
S storage c;
while(false) {
}
c = s;
c;
}
}
// ----

View File

@ -5,4 +5,4 @@ contract C {
}
}
// ----
// DeclarationError: (52-85): Uninitialized storage pointer.
// TypeError: (95-96): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -8,4 +8,4 @@ contract C {
}
}
// ----
// DeclarationError: (84-95): Uninitialized storage pointer.
// TypeError: (105-106): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -3,4 +3,3 @@ contract c {
}
// ----
// TypeError: (39-58): Type int_const 7 is not implicitly convertible to expected type contract c[10] storage pointer.
// DeclarationError: (60-83): Uninitialized storage pointer.