From da3ac8640328c15872630d5d86976f17480f9492 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 7 Aug 2017 15:44:35 +0200 Subject: [PATCH] Warn about large storage structures. --- Changelog.md | 1 + libsolidity/analysis/StaticAnalyzer.cpp | 42 +++++++++++++++ libsolidity/analysis/StaticAnalyzer.h | 3 ++ .../SolidityNameAndTypeResolution.cpp | 51 +++++++++++++++++++ 4 files changed, 97 insertions(+) diff --git a/Changelog.md b/Changelog.md index 4609da921..cb6720777 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: * Parser: Display previous visibility specifier in error if multiple are found. * Syntax Checker: Support ``pragma experimental ;`` to turn on experimental features. + * Static Analyzer: Warn about large storage structures. * Metadata: Store experimental flag in metadata CBOR. Bugfixes: diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 46477e1e0..ab1cbb526 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -92,6 +92,17 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable) // This is not a no-op, the entry might pre-exist. m_localVarUseCount[&_variable] += 0; } + else if (_variable.isStateVariable()) + { + set structsSeen; + if (structureSizeEstimate(*_variable.type(), structsSeen) >= bigint(1) << 64) + m_errorReporter.warning( + _variable.location(), + "Variable covers a large part of storage and thus makes collisions likely. " + "Either use mappings or dynamic arrays and allow their size to be increased only " + "in small quantities per transaction." + ); + } return true; } @@ -160,3 +171,34 @@ bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly) return true; } + +bigint StaticAnalyzer::structureSizeEstimate(Type const& _type, set& _structsSeen) +{ + switch (_type.category()) + { + case Type::Category::Array: + { + auto const& t = dynamic_cast(_type); + return structureSizeEstimate(*t.baseType(), _structsSeen) * (t.isDynamicallySized() ? 1 : t.length()); + } + case Type::Category::Struct: + { + auto const& t = dynamic_cast(_type); + bigint size = 1; + if (!_structsSeen.count(&t.structDefinition())) + { + _structsSeen.insert(&t.structDefinition()); + for (auto const& m: t.members(nullptr)) + size += structureSizeEstimate(*m.type, _structsSeen); + } + return size; + } + case Type::Category::Mapping: + { + return structureSizeEstimate(*dynamic_cast(_type).valueType(), _structsSeen); + } + default: + break; + } + return bigint(1); +} diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index 21a487df4..a3080b428 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -65,6 +65,9 @@ private: virtual bool visit(MemberAccess const& _memberAccess) override; virtual bool visit(InlineAssembly const& _inlineAssembly) override; + /// @returns the size of this type in storage, including all sub-types. + static bigint structureSizeEstimate(Type const& _type, std::set& _structsSeen); + ErrorReporter& m_errorReporter; /// Flag that indicates whether the current contract definition is a library. diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index f7648bd7a..5c01a2457 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6536,6 +6536,57 @@ BOOST_AUTO_TEST_CASE(constructor_without_implementation) CHECK_ERROR(text, TypeError, "Constructor must be implemented if declared."); } +BOOST_AUTO_TEST_CASE(large_storage_array_fine) +{ + char const* text = R"( + contract C { + uint[2**64 - 1] x; + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(large_storage_array_simple) +{ + char const* text = R"( + contract C { + uint[2**64] x; + } + )"; + CHECK_WARNING(text, "covers a large part of storage and thus makes collisions likely"); +} + +BOOST_AUTO_TEST_CASE(large_storage_arrays_combined) +{ + char const* text = R"( + contract C { + uint[200][200][2**30][][2**30] x; + } + )"; + CHECK_WARNING(text, "covers a large part of storage and thus makes collisions likely"); +} + +BOOST_AUTO_TEST_CASE(large_storage_arrays_struct) +{ + char const* text = R"( + contract C { + struct S { uint[2**30] x; uint[2**50] y; } + S[2**20] x; + } + )"; + CHECK_WARNING(text, "covers a large part of storage and thus makes collisions likely"); +} + +BOOST_AUTO_TEST_CASE(large_storage_array_mapping) +{ + char const* text = R"( + contract C { + mapping(uint => uint[2**100]) x; + } + )"; + CHECK_WARNING(text, "covers a large part of storage and thus makes collisions likely"); +} + BOOST_AUTO_TEST_CASE(library_function_without_implementation) { char const* text = R"(