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

+61
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

+2
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

+1
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.
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

+1
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.
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.
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.
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)