Skip to content

Commit 4bf8a9f

Browse files
authored
Merge pull request #15912 from ethereum/warnStorageLayoutNearTheEnd
Warn when the storage layout base is near the end of storage (2^64 slots or less)
2 parents 07b202c + 5297950 commit 4bf8a9f

8 files changed

+76
-0
lines changed

libsolidity/analysis/PostTypeContractLevelChecker.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#include <libsolutil/FunctionSelector.h>
3030
#include <liblangutil/ErrorReporter.h>
3131

32+
#include <range/v3/action/reverse.hpp>
33+
3234
#include <limits>
3335

3436
using namespace solidity;
@@ -76,6 +78,8 @@ bool PostTypeContractLevelChecker::check(ContractDefinition const& _contract)
7678
if (_contract.storageLayoutSpecifier())
7779
checkStorageLayoutSpecifier(_contract);
7880

81+
warnStorageLayoutBaseNearStorageEnd(_contract);
82+
7983
return !Error::containsErrors(m_errorReporter.errors());
8084
}
8185

@@ -145,3 +149,60 @@ void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(ContractDefinitio
145149
"Contract extends past the end of storage when this base slot value is specified."
146150
);
147151
}
152+
153+
namespace
154+
{
155+
156+
VariableDeclaration const* findLastStorageVariable(ContractDefinition const& _contract)
157+
{
158+
for (ContractDefinition const* baseContract: ranges::actions::reverse(_contract.annotation().linearizedBaseContracts))
159+
for (VariableDeclaration const* stateVariable: ranges::actions::reverse(baseContract->stateVariables()))
160+
if (stateVariable->referenceLocation() == VariableDeclaration::Location::Unspecified)
161+
return stateVariable;
162+
163+
return nullptr;
164+
}
165+
166+
}
167+
168+
void PostTypeContractLevelChecker::warnStorageLayoutBaseNearStorageEnd(ContractDefinition const& _contract)
169+
{
170+
// In case of most errors the warning is pointless. E.g. if we're already past storage end.
171+
// If the errors were in the layout specifier, we may not even be able to get values to validate.
172+
if (Error::containsErrors(m_errorReporter.errors()))
173+
return;
174+
175+
bigint storageSize = contractStorageSizeUpperBound(_contract, VariableDeclaration::Location::Unspecified);
176+
u256 baseSlot = layoutBaseForInheritanceHierarchy(_contract, DataLocation::Storage);
177+
solAssert(baseSlot + storageSize <= std::numeric_limits<u256>::max());
178+
179+
if (
180+
u256 slotsLeft = std::numeric_limits<u256>::max() - baseSlot - u256(storageSize);
181+
slotsLeft <= u256(1) << 64
182+
)
183+
{
184+
auto const& location = _contract.storageLayoutSpecifier() ?
185+
_contract.storageLayoutSpecifier()->location() :
186+
_contract.location();
187+
188+
VariableDeclaration const* lastStorageVariable = findLastStorageVariable(_contract);
189+
190+
auto errorID = 3495_error;
191+
std::string errorMsg = "This contract is very close to the end of storage. This limits its future upgradability.";
192+
if (lastStorageVariable)
193+
m_errorReporter.warning(
194+
errorID,
195+
location,
196+
errorMsg,
197+
SecondarySourceLocation{}.append(
198+
fmt::format(
199+
"There are {} storage slots between this state variable and the end of storage.",
200+
formatNumberReadable(slotsLeft)
201+
),
202+
lastStorageVariable->location()
203+
)
204+
);
205+
else
206+
m_errorReporter.warning(errorID, location, errorMsg);
207+
}
208+
}

libsolidity/analysis/PostTypeContractLevelChecker.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class PostTypeContractLevelChecker
5252

5353
void checkStorageLayoutSpecifier(ContractDefinition const& _contract);
5454

55+
void warnStorageLayoutBaseNearStorageEnd(ContractDefinition const& _contract);
56+
5557
langutil::ErrorReporter& m_errorReporter;
5658
};
5759

test/libsolidity/syntaxTests/largeTypes/max_size_array_with_transient_state_variables.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ contract C {
55
// ====
66
// EVMVersion: >=cancun
77
// ----
8+
// Warning 3495: (0-60): This contract is very close to the end of storage. This limits its future upgradability.
89
// Warning 7325: (17-33): Type uint256[115792089237316195423570985008687907853269984665640564039457584007913129639935] 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.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
contract C layout at 2**256 - 1 {
22
}
33
// ----
4+
// Warning 3495: (11-31): This contract is very close to the end of storage. This limits its future upgradability.

test/libsolidity/syntaxTests/storageLayoutSpecifier/contract_at_storage_end_with_transient_state_variables.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ contract C layout at 2**256 - 1 {
66
// ====
77
// EVMVersion: >=cancun
88
// ----
9+
// Warning 3495: (11-31): This contract is very close to the end of storage. This limits its future upgradability.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
contract A layout at (2**256 + 1) * 2 - 2**256 - 3 {}
22
contract B layout at (2**2 - 2**3) * (2**5 - 2**8) {}
33
// ----
4+
// Warning 3495: (11-51): This contract is very close to the end of storage. This limits its future upgradability.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
contract C layout at 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF {}
22
// ----
3+
// Warning 3495: (11-87): This contract is very close to the end of storage. This limits its future upgradability.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
contract A layout at 2**256 - 2**64 {}
2+
contract C layout at 2**256 - 2**65 {
3+
uint[2**63] x;
4+
uint[2**63] y;
5+
}
6+
// ----
7+
// Warning 3495: (11-35): This contract is very close to the end of storage. This limits its future upgradability.
8+
// Warning 3495: (50-74): This contract is very close to the end of storage. This limits its future upgradability.

0 commit comments

Comments
 (0)