Skip to content

Commit b57d7a2

Browse files
committed
wip
1 parent 0d1f552 commit b57d7a2

13 files changed

+291
-114
lines changed

docs/bugs.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[
22
{
3-
"uid": "SOL-2025-4",
3+
"uid": "SOL-2025-1",
44
"name": "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary",
55
"summary": "Fixed-length storage arrays crossing the 2^256 slot boundary can exhibit unexpected behavior when cleared (using the delete operator) or partially assigned, leading to silent data retention and inconsistent results.",
66
"description": "Large static arrays in storage risk overlapping the 2^256 storage slot boundary. Partial assignments or delete operations may not properly reset all elements in such conditions, causing inconsistency during deletion and unexpected data retention. Although such situations are exceedingly rare in typical contracts, overflow on array deletion become more plausible when arrays are extremely large or the storage is manually positioned close to the storage boundaries. The compiler already warns about potential storage collisions in such scenarios.",

test/libsolidity/semanticTests/storage/delete_overflow_bug.sol

-19
This file was deleted.

test/libsolidity/semanticTests/storage/delete_overflow_bug_collision.sol

-29
This file was deleted.

test/libsolidity/semanticTests/storage/delete_overflow_bug_large_mapping_storage_boundary.sol

-24
This file was deleted.

test/libsolidity/semanticTests/storage/delete_overflow_bug_partial_assignment.sol

-19
This file was deleted.

test/libsolidity/semanticTests/storage/delete_overflow_bug_storage_layout.sol

-22
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
contract C {
2+
function getArray() internal pure returns (uint256[10][1] storage _x) {
3+
assembly {
4+
_x.slot := sub(0, 5)
5+
}
6+
}
7+
8+
function assignArray(uint256[10] memory y) public {
9+
uint256[10][1] storage _x = getArray();
10+
_x[0] = y;
11+
}
12+
13+
function x() public view returns (uint256[10] memory) {
14+
return getArray()[0];
15+
}
16+
}
17+
// ----
18+
// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
19+
// assignArray(uint256[10]): 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ->
20+
// gas irOptimized: 245236
21+
// gas legacyOptimized: 245441
22+
// x() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
23+
// assignArray(uint256[10]): 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ->
24+
// x() -> 10, 0x14, 0x1e, 0x28, 0x32, 0x3c, 0x46, 0x50, 0x5a, 0x64
25+
// gas irOptimized: 198025
26+
// gas legacy: 200268
27+
// gas legacyOptimized: 198058
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
contract C {
2+
function getArray() internal pure returns (uint256[10][1] storage _x) {
3+
assembly {
4+
_x.slot := sub(0, 5)
5+
}
6+
}
7+
8+
function fillArray() public {
9+
uint256[10][1] storage _x = getArray();
10+
for (uint i = 1; i < 10; i++)
11+
_x[0][i] = i;
12+
}
13+
14+
function clearArray() public {
15+
uint256[10][1] storage _x = getArray();
16+
delete _x[0];
17+
}
18+
19+
function x() public view returns (uint256[10] memory) {
20+
return getArray()[0];
21+
}
22+
}
23+
// ----
24+
// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
25+
// fillArray()
26+
// gas irOptimized: 220705
27+
// gas legacyOptimized: 220871
28+
// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
29+
// clearArray()
30+
// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
31+
// gas irOptimized: 181920
32+
// gas legacy: 184143
33+
// gas legacyOptimized: 181899
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
contract C {
2+
uint256 public y = 42;
3+
4+
function getArray() internal pure returns (uint256[10][1] storage _x) {
5+
assembly {
6+
_x.slot := sub(0, 5)
7+
}
8+
}
9+
10+
function fillArray() public {
11+
uint256[10][1] storage _x = getArray();
12+
for (uint i = 1; i < 10; i++)
13+
_x[0][i] = i;
14+
}
15+
16+
function clearArray() public {
17+
uint256[10][1] storage _x = getArray();
18+
delete _x[0];
19+
}
20+
21+
function x() public view returns (uint256[10] memory) {
22+
return getArray()[0];
23+
}
24+
}
25+
26+
// ----
27+
// y() -> 42
28+
// x() -> 0, 0, 0, 0, 0, 0x2a, 0, 0, 0, 0
29+
// fillArray()
30+
// gas irOptimized: 203627
31+
// gas legacyOptimized: 203793
32+
// y() -> 5
33+
// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
34+
// clearArray()
35+
// y() -> 0
36+
// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
37+
// gas irOptimized: 168243
38+
// gas legacy: 170463
39+
// gas legacyOptimized: 168219
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
contract C {
2+
function getArray() internal pure returns (uint256[10][1] storage _x) {
3+
assembly {
4+
_x.slot := sub(0, 5)
5+
}
6+
}
7+
8+
function fillArray() public {
9+
uint256[10][1] storage _x = getArray();
10+
for (uint i = 1; i < 10; i++)
11+
_x[0][i] = i;
12+
}
13+
14+
function x() public view returns (uint256[10] memory) {
15+
return getArray()[0];
16+
}
17+
18+
function partialAssignArray() public {
19+
uint256[10][1] storage _x = getArray();
20+
_x[0] = [11, 12, 13];
21+
}
22+
}
23+
// ----
24+
// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
25+
// fillArray()
26+
// gas irOptimized: 220705
27+
// gas legacyOptimized: 220871
28+
// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
29+
// partialAssignArray()
30+
// x() -> 11, 12, 13, 0, 0, 0, 0, 0, 0, 0
31+
// gas irOptimized: 198025
32+
// gas legacy: 200268
33+
// gas legacyOptimized: 198058
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
contract C {
2+
function getArray() internal pure returns (uint256[10][1] storage _x) {
3+
assembly {
4+
_x.slot := sub(0, 5)
5+
}
6+
}
7+
8+
function fillArray() public {
9+
uint256[10][1] storage _x = getArray();
10+
for (uint i = 1; i < 10; i++)
11+
_x[0][i] = i;
12+
}
13+
14+
function x() public view returns (uint256[10] memory) {
15+
return getArray()[0];
16+
}
17+
18+
function partialAssignArray() public {
19+
uint256[10][1] storage _x = getArray();
20+
_x[0] = [11, 12, 13, 14, 15, 16, 17];
21+
}
22+
}
23+
// ----
24+
// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
25+
// fillArray()
26+
// gas irOptimized: 220705
27+
// gas legacyOptimized: 220871
28+
// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
29+
// partialAssignArray()
30+
// x() -> 11, 12, 13, 14, 15, 0x10, 0x11, 0, 0, 0
31+
// gas irOptimized: 198025
32+
// gas legacy: 200268
33+
// gas legacyOptimized: 198058
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
contract C layout at 2**256 - 5 {
2+
uint256 a;
3+
4+
function getArray() internal pure returns (uint256[10][1] storage _x) {
5+
assembly {
6+
_x.slot := a.slot
7+
}
8+
}
9+
10+
function fillArray() public {
11+
uint256[10][1] storage _x = getArray();
12+
for (uint i = 1; i < 10; i++)
13+
_x[0][i] = i;
14+
}
15+
16+
function partialAssignArrayBeforeStorageBoundary() public {
17+
uint256[10][1] storage _x = getArray();
18+
_x[0] = [11, 12, 13];
19+
}
20+
21+
function partialAssignArrayCrossStorageBoundary() public {
22+
uint256[10][1] storage _x = getArray();
23+
_x[0] = [14, 15, 16, 17, 18, 19, 20];
24+
}
25+
26+
function clearArray() public {
27+
uint256[10][1] storage _x = getArray();
28+
delete _x[0];
29+
}
30+
31+
function x() public view returns (uint256[10] memory) {
32+
return getArray()[0];
33+
}
34+
}
35+
// ----
36+
// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
37+
// fillArray()
38+
// gas irOptimized: 220749
39+
// gas legacyOptimized: 220915
40+
// partialAssignArrayBeforeStorageBoundary()
41+
// x() -> 11, 12, 13, 0, 0, 0, 0, 0, 0, 0
42+
// fillArray()
43+
// gas irOptimized: 186549
44+
// gas legacyOptimized: 186715
45+
// x() -> 11, 1, 2, 3, 4, 5, 6, 7, 8, 9
46+
// partialAssignArrayCrossStorageBoundary()
47+
// x() -> 14, 15, 0x10, 0x11, 0x12, 0x13, 0x14, 0, 0, 0
48+
// clearArray()
49+
// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
50+
// gas irOptimized: 181930
51+
// gas legacy: 184143
52+
// gas legacyOptimized: 181900

0 commit comments

Comments
 (0)