-
Notifications
You must be signed in to change notification settings - Fork 6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Warn when the storage layout base is near the end of storage (2^64 slots or less) #15912
base: develop
Are you sure you want to change the base?
Changes from all commits
f7afdad
30a0d8a
821ee1d
f27af62
02b9eca
acb5733
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -73,15 +73,17 @@ bool PostTypeContractLevelChecker::check(ContractDefinition const& _contract) | |||||
errorHashes[hash][signature] = error->location(); | ||||||
} | ||||||
|
||||||
if (auto const* layoutSpecifier = _contract.storageLayoutSpecifier()) | ||||||
checkStorageLayoutSpecifier(*layoutSpecifier); | ||||||
if (_contract.storageLayoutSpecifier()) | ||||||
checkStorageLayoutSpecifier(_contract); | ||||||
|
||||||
return !Error::containsErrors(m_errorReporter.errors()); | ||||||
} | ||||||
|
||||||
void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(StorageLayoutSpecifier const& _storageLayoutSpecifier) | ||||||
void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(ContractDefinition const& _contract) | ||||||
{ | ||||||
Expression const& baseSlotExpression = _storageLayoutSpecifier.baseSlotExpression(); | ||||||
StorageLayoutSpecifier const* storageLayoutSpecifier = _contract.storageLayoutSpecifier(); | ||||||
solAssert(storageLayoutSpecifier); | ||||||
Expression const& baseSlotExpression = storageLayoutSpecifier->baseSlotExpression(); | ||||||
|
||||||
if (!*baseSlotExpression.annotation().isPure) | ||||||
{ | ||||||
|
@@ -117,22 +119,41 @@ void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(StorageLayoutSpec | |||||
} | ||||||
solAssert(rationalType->value().denominator() == 1); | ||||||
|
||||||
if ( | ||||||
rationalType->value().numerator() < 0 || | ||||||
rationalType->value().numerator() > std::numeric_limits<u256>::max() | ||||||
) | ||||||
bigint baseSlot = rationalType->value().numerator(); | ||||||
if (!(0 <= baseSlot && baseSlot <= std::numeric_limits<u256>::max())) | ||||||
{ | ||||||
m_errorReporter.typeError( | ||||||
6753_error, | ||||||
baseSlotExpression.location(), | ||||||
fmt::format( | ||||||
"The base slot of the storage layout evaluates to {}, which is outside the range of type uint256.", | ||||||
formatNumberReadable(rationalType->value().numerator()) | ||||||
formatNumberReadable(baseSlot) | ||||||
) | ||||||
); | ||||||
return; | ||||||
} | ||||||
|
||||||
solAssert(baseSlotExpressionType->isImplicitlyConvertibleTo(*TypeProvider::uint256())); | ||||||
_storageLayoutSpecifier.annotation().baseSlot = u256(rationalType->value().numerator()); | ||||||
storageLayoutSpecifier->annotation().baseSlot = u256(baseSlot); | ||||||
|
||||||
if ( | ||||||
u256 slotsLeft = std::numeric_limits<u256>::max() - *storageLayoutSpecifier->annotation().baseSlot; | ||||||
slotsLeft <= u256(1) << 64 | ||||||
) | ||||||
m_errorReporter.warning( | ||||||
3495_error, | ||||||
storageLayoutSpecifier->baseSlotExpression().location(), | ||||||
fmt::format( | ||||||
"There are {} slots before the end of the contract storage when this specified base layout is used.", | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about this?
Suggested change
Like I said above, I'd put the number of slots left in the secondary message, pointing at a specific variable. |
||||||
formatNumberReadable(slotsLeft) | ||||||
)); | ||||||
|
||||||
bigint size = contractStorageSizeUpperBound(_contract, VariableDeclaration::Location::Unspecified); | ||||||
solAssert(size < bigint(1) << 256); | ||||||
if (baseSlot + size >= bigint(1) << 256) | ||||||
m_errorReporter.typeError( | ||||||
5015_error, | ||||||
baseSlotExpression.location(), | ||||||
"Contract extends past the end of storage when this base slot value is specified." | ||||||
); | ||||||
} |
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
--optimize --ir-optimized --debug-info none - |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
Optimized IR: | ||
/// @use-src 0:"<stdin>" | ||
object "C_7" { | ||
code { | ||
{ | ||
let _1 := memoryguard(0x80) | ||
mstore(64, _1) | ||
if callvalue() { revert(0, 0) } | ||
sstore(0x2a, 0x0a) | ||
let _2 := datasize("C_7_deployed") | ||
codecopy(_1, dataoffset("C_7_deployed"), _2) | ||
return(_1, _2) | ||
} | ||
} | ||
/// @use-src 0:"<stdin>" | ||
object "C_7_deployed" { | ||
code { { revert(0, 0) } } | ||
data ".metadata" hex"<BYTECODE REMOVED>" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
//SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity >=0.0; | ||
|
||
contract C layout at 42 {} | ||
contract C layout at 42 { | ||
uint x = 10; | ||
} |
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
--no-cbor-metadata --optimize --asm --debug-info none - |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
|
||
======= <stdin>:C ======= | ||
EVM assembly: | ||
mstore(0x40, 0x80) | ||
callvalue | ||
dup1 | ||
iszero | ||
tag_1 | ||
jumpi | ||
revert(0x00, 0x00) | ||
tag_1: | ||
pop | ||
dataSize(sub_0) | ||
dup1 | ||
dataOffset(sub_0) | ||
0x00 | ||
codecopy | ||
0x00 | ||
return | ||
stop | ||
|
||
sub_0: assembly { | ||
mstore(0x40, 0x80) | ||
callvalue | ||
dup1 | ||
iszero | ||
tag_1 | ||
jumpi | ||
revert(0x00, 0x00) | ||
tag_1: | ||
pop | ||
jumpi(tag_2, lt(calldatasize, 0x04)) | ||
shr(0xe0, calldataload(0x00)) | ||
dup1 | ||
0x26121ff0 | ||
eq | ||
tag_3 | ||
jumpi | ||
tag_2: | ||
revert(0x00, 0x00) | ||
tag_3: | ||
tag_4 | ||
tag_5 | ||
jump // in | ||
tag_4: | ||
stop | ||
tag_5: | ||
sload(0x0abc) | ||
tag_7 | ||
swap1 | ||
0x01 | ||
tag_8 | ||
jump // in | ||
tag_7: | ||
0x0abc | ||
sstore | ||
jump // out | ||
tag_8: | ||
dup1 | ||
dup3 | ||
add | ||
dup1 | ||
dup3 | ||
gt | ||
iszero | ||
tag_11 | ||
jumpi | ||
0x4e487b71 | ||
0xe0 | ||
shl | ||
0x00 | ||
mstore | ||
0x11 | ||
0x04 | ||
mstore | ||
0x24 | ||
0x00 | ||
revert | ||
tag_11: | ||
swap3 | ||
swap2 | ||
pop | ||
pop | ||
jump // out | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity >=0.0.0; | ||
|
||
contract C layout at 0xABC { | ||
uint x; | ||
function f() public { | ||
x = x + 1; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
--storage-layout --pretty-json --json-indent 4 - |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that I think of it, this check is not really limited to contracts with a layout. The dangerous thing is leaving too few slots between the last storage variable and the storage end, regardless of the base slot. It's more likely to happen if you have a non-zero base slot, but technically, you could end up in that situation also by just having large enough variables. And you should get this warning even if you're already getting the one for oversized variables because they warn about different issues.
Let's do this:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, not sure about transient storage. There are no upgradability issues there so I wonder if we should not warn there or if the fact that this is so close to the edge still warrants a warning...