diff --git a/Changelog.md b/Changelog.md index 525ed2d9e58b..41e24183416f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,9 @@ Language Features: +Important Bugfixes: +* Code Generator: Fix inconsistent handling of storage arrays at the slot overflow boundary, which could lead to incorrect storage cleanup when using `delete` or partial assignments of arrays. + Compiler Features: * NatSpec: Capture Natspec documentation of `enum` values in the AST. diff --git a/docs/bugs.json b/docs/bugs.json index 800524669048..b1194435658a 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,4 +1,14 @@ [ + { + "uid": "SOL-2025-1", + "name": "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", + "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.", + "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.", + "link": "TODO", + "introduced": "0.1.1", + "fixed": "0.8.30", + "severity": "low" + }, { "uid": "SOL-2023-3", "name": "VerbatimInvalidDeduplication", diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 60fcdd049a0f..14856820f688 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -23,6 +23,7 @@ }, "0.1.1": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -45,6 +46,7 @@ }, "0.1.2": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -67,6 +69,7 @@ }, "0.1.3": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -89,6 +92,7 @@ }, "0.1.4": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -112,6 +116,7 @@ }, "0.1.5": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -135,6 +140,7 @@ }, "0.1.6": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -160,6 +166,7 @@ }, "0.1.7": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -185,6 +192,7 @@ }, "0.2.0": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -211,6 +219,7 @@ }, "0.2.1": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -237,6 +246,7 @@ }, "0.2.2": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -263,6 +273,7 @@ }, "0.3.0": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -291,6 +302,7 @@ }, "0.3.1": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -318,6 +330,7 @@ }, "0.3.2": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -345,6 +358,7 @@ }, "0.3.3": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -371,6 +385,7 @@ }, "0.3.4": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -397,6 +412,7 @@ }, "0.3.5": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -423,6 +439,7 @@ }, "0.3.6": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -447,6 +464,7 @@ }, "0.4.0": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -471,6 +489,7 @@ }, "0.4.1": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -495,6 +514,7 @@ }, "0.4.10": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -518,6 +538,7 @@ }, "0.4.11": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -540,6 +561,7 @@ }, "0.4.12": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -561,6 +583,7 @@ }, "0.4.13": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -582,6 +605,7 @@ }, "0.4.14": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -602,6 +626,7 @@ }, "0.4.15": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -621,6 +646,7 @@ }, "0.4.16": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -643,6 +669,7 @@ }, "0.4.17": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -666,6 +693,7 @@ }, "0.4.18": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -688,6 +716,7 @@ }, "0.4.19": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -711,6 +740,7 @@ }, "0.4.2": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -734,6 +764,7 @@ }, "0.4.20": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -757,6 +788,7 @@ }, "0.4.21": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -780,6 +812,7 @@ }, "0.4.22": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -803,6 +836,7 @@ }, "0.4.23": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -825,6 +859,7 @@ }, "0.4.24": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -847,6 +882,7 @@ }, "0.4.25": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -867,6 +903,7 @@ }, "0.4.26": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -884,6 +921,7 @@ }, "0.4.3": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -906,6 +944,7 @@ }, "0.4.4": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -927,6 +966,7 @@ }, "0.4.5": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -951,6 +991,7 @@ }, "0.4.6": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -974,6 +1015,7 @@ }, "0.4.7": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -997,6 +1039,7 @@ }, "0.4.8": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -1020,6 +1063,7 @@ }, "0.4.9": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -1043,6 +1087,7 @@ }, "0.5.0": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1063,6 +1108,7 @@ }, "0.5.1": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1083,6 +1129,7 @@ }, "0.5.10": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1101,6 +1148,7 @@ }, "0.5.11": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1118,6 +1166,7 @@ }, "0.5.12": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1135,6 +1184,7 @@ }, "0.5.13": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1152,6 +1202,7 @@ }, "0.5.14": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1171,6 +1222,7 @@ }, "0.5.15": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1189,6 +1241,7 @@ }, "0.5.16": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1206,6 +1259,7 @@ }, "0.5.17": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1222,6 +1276,7 @@ }, "0.5.2": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1242,6 +1297,7 @@ }, "0.5.3": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1262,6 +1318,7 @@ }, "0.5.4": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1282,6 +1339,7 @@ }, "0.5.5": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1304,6 +1362,7 @@ }, "0.5.6": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1326,6 +1385,7 @@ }, "0.5.7": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1346,6 +1406,7 @@ }, "0.5.8": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1367,6 +1428,7 @@ }, "0.5.9": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1387,6 +1449,7 @@ }, "0.6.0": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1405,6 +1468,7 @@ }, "0.6.1": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1422,6 +1486,7 @@ }, "0.6.10": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1438,6 +1503,7 @@ }, "0.6.11": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1454,6 +1520,7 @@ }, "0.6.12": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1470,6 +1537,7 @@ }, "0.6.2": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1488,6 +1556,7 @@ }, "0.6.3": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1506,6 +1575,7 @@ }, "0.6.4": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1524,6 +1594,7 @@ }, "0.6.5": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1542,6 +1613,7 @@ }, "0.6.6": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1559,6 +1631,7 @@ }, "0.6.7": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1577,6 +1650,7 @@ }, "0.6.8": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1592,6 +1666,7 @@ }, "0.6.9": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1609,6 +1684,7 @@ }, "0.7.0": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1625,6 +1701,7 @@ }, "0.7.1": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1642,6 +1719,7 @@ }, "0.7.2": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1658,6 +1736,7 @@ }, "0.7.3": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1673,6 +1752,7 @@ }, "0.7.4": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1687,6 +1767,7 @@ }, "0.7.5": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1701,6 +1782,7 @@ }, "0.7.6": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1715,6 +1797,7 @@ }, "0.8.0": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1729,6 +1812,7 @@ }, "0.8.1": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1743,6 +1827,7 @@ }, "0.8.10": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1755,6 +1840,7 @@ }, "0.8.11": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1768,6 +1854,7 @@ }, "0.8.12": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1781,6 +1868,7 @@ }, "0.8.13": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1795,6 +1883,7 @@ }, "0.8.14": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1807,6 +1896,7 @@ }, "0.8.15": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1817,6 +1907,7 @@ }, "0.8.16": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1826,6 +1917,7 @@ }, "0.8.17": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" @@ -1834,6 +1926,7 @@ }, "0.8.18": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" @@ -1842,6 +1935,7 @@ }, "0.8.19": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" @@ -1850,6 +1944,7 @@ }, "0.8.2": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1864,6 +1959,7 @@ }, "0.8.20": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" @@ -1872,46 +1968,63 @@ }, "0.8.21": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication" ], "released": "2023-07-19" }, "0.8.22": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication" ], "released": "2023-10-25" }, "0.8.23": { - "bugs": [], + "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary" + ], "released": "2023-11-08" }, "0.8.24": { - "bugs": [], + "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary" + ], "released": "2024-01-25" }, "0.8.25": { - "bugs": [], + "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary" + ], "released": "2024-03-14" }, "0.8.26": { - "bugs": [], + "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary" + ], "released": "2024-05-21" }, "0.8.27": { - "bugs": [], + "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary" + ], "released": "2024-09-04" }, "0.8.28": { - "bugs": [], + "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary" + ], "released": "2024-10-09" }, "0.8.29": { - "bugs": [], + "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary" + ], "released": "2025-03-12" }, "0.8.3": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1925,6 +2038,7 @@ }, "0.8.4": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1937,6 +2051,7 @@ }, "0.8.5": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1950,6 +2065,7 @@ }, "0.8.6": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1963,6 +2079,7 @@ }, "0.8.7": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1976,6 +2093,7 @@ }, "0.8.8": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1990,6 +2108,7 @@ }, "0.8.9": { "bugs": [ + "InconsistentTreatmentOfStorageArraysOnSlotOverflowBoundary", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 455cfe0842ed..962019a6ca60 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -289,10 +289,15 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons // stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end _context << Instruction::POP << Instruction::SWAP1 << Instruction::POP; // stack: target_ref target_data_end target_data_pos_updated - if (targetBaseType->storageBytes() < 32) - utils.clearStorageLoop(TypeProvider::uint256()); + if (_targetType.isDynamicallySized()) + ArrayUtils(_context).clearDynamicArray(_targetType); else - utils.clearStorageLoop(targetBaseType); + { + if (targetBaseType->storageBytes() < 32) + utils.clearStorageLoop(TypeProvider::uint256(), /* _canOverflow */ true); + else + utils.clearStorageLoop(targetBaseType, /* _canOverflow */ true); + } _context << Instruction::POP; } ); @@ -590,9 +595,9 @@ void ArrayUtils::clearArray(ArrayType const& _typeIn) const ArrayUtils(_context).convertLengthToSize(_type); _context << Instruction::ADD << Instruction::SWAP1; if (_type.baseType()->storageBytes() < 32) - ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256()); + ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256(), !_type.isDynamicallySized()); else - ArrayUtils(_context).clearStorageLoop(_type.baseType()); + ArrayUtils(_context).clearStorageLoop(_type.baseType(), !_type.isDynamicallySized()); _context << Instruction::POP; } solAssert(_context.stackHeight() == stackHeightStart - 2, ""); @@ -631,164 +636,14 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const << Instruction::SWAP1; // stack: data_pos_end data_pos if (_type.storageStride() < 32) - clearStorageLoop(TypeProvider::uint256()); + clearStorageLoop(TypeProvider::uint256(), /* _canOverflow */ false); else - clearStorageLoop(_type.baseType()); + clearStorageLoop(_type.baseType(), /* _canOverflow */ false); // cleanup m_context << endTag; m_context << Instruction::POP; } -void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const -{ - Type const* type = &_typeIn; - m_context.callLowLevelFunction( - "$resizeDynamicArray_" + _typeIn.identifier(), - 2, - 0, - [type](CompilerContext& _context) - { - ArrayType const& _type = dynamic_cast(*type); - solAssert(_type.location() == DataLocation::Storage, ""); - solAssert(_type.isDynamicallySized(), ""); - if (!_type.isByteArrayOrString() && _type.baseType()->storageBytes() < 32) - solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); - - unsigned stackHeightStart = _context.stackHeight(); - evmasm::AssemblyItem resizeEnd = _context.newTag(); - - // stack: ref new_length - // fetch old length - ArrayUtils(_context).retrieveLength(_type, 1); - // stack: ref new_length old_length - solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "2"); - - // Special case for short byte arrays, they are stored together with their length - if (_type.isByteArrayOrString()) - { - evmasm::AssemblyItem regularPath = _context.newTag(); - // We start by a large case-distinction about the old and new length of the byte array. - - _context << Instruction::DUP3 << Instruction::SLOAD; - // stack: ref new_length current_length ref_value - - solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - _context << Instruction::DUP2 << u256(31) << Instruction::LT; - evmasm::AssemblyItem currentIsLong = _context.appendConditionalJump(); - _context << Instruction::DUP3 << u256(31) << Instruction::LT; - evmasm::AssemblyItem newIsLong = _context.appendConditionalJump(); - - // Here: short -> short - - // Compute 1 << (256 - 8 * new_size) - evmasm::AssemblyItem shortToShort = _context.newTag(); - _context << shortToShort; - _context << Instruction::DUP3 << u256(8) << Instruction::MUL; - _context << u256(0x100) << Instruction::SUB; - _context << u256(2) << Instruction::EXP; - // Divide and multiply by that value, clearing bits. - _context << Instruction::DUP1 << Instruction::SWAP2; - _context << Instruction::DIV << Instruction::MUL; - // Insert 2*length. - _context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD; - _context << Instruction::OR; - // Store. - _context << Instruction::DUP4 << Instruction::SSTORE; - solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3"); - _context.appendJumpTo(resizeEnd); - - _context.adjustStackOffset(1); // we have to do that because of the jumps - // Here: short -> long - - _context << newIsLong; - // stack: ref new_length current_length ref_value - solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - // Zero out lower-order byte. - _context << u256(0xff) << Instruction::NOT << Instruction::AND; - // Store at data location. - _context << Instruction::DUP4; - CompilerUtils(_context).computeHashStatic(); - _context << Instruction::SSTORE; - // stack: ref new_length current_length - // Store new length: Compute 2*length + 1 and store it. - _context << Instruction::DUP2 << Instruction::DUP1 << Instruction::ADD; - _context << u256(1) << Instruction::ADD; - // stack: ref new_length current_length 2*new_length+1 - _context << Instruction::DUP4 << Instruction::SSTORE; - solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3"); - _context.appendJumpTo(resizeEnd); - - _context.adjustStackOffset(1); // we have to do that because of the jumps - - _context << currentIsLong; - _context << Instruction::DUP3 << u256(31) << Instruction::LT; - _context.appendConditionalJumpTo(regularPath); - - // Here: long -> short - // Read the first word of the data and store it on the stack. Clear the data location and - // then jump to the short -> short case. - - // stack: ref new_length current_length ref_value - solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - _context << Instruction::POP << Instruction::DUP3; - CompilerUtils(_context).computeHashStatic(); - _context << Instruction::DUP1 << Instruction::SLOAD << Instruction::SWAP1; - // stack: ref new_length current_length first_word data_location - _context << Instruction::DUP3; - ArrayUtils(_context).convertLengthToSize(_type); - _context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1; - // stack: ref new_length current_length first_word data_location_end data_location - ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256()); - _context << Instruction::POP; - // stack: ref new_length current_length first_word - solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - _context.appendJumpTo(shortToShort); - - _context << regularPath; - // stack: ref new_length current_length ref_value - _context << Instruction::POP; - } - - // Change of length for a regular array (i.e. length at location, data at KECCAK256(location)). - // stack: ref new_length old_length - // store new length - _context << Instruction::DUP2; - if (_type.isByteArrayOrString()) - // For a "long" byte array, store length as 2*length+1 - _context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD; - _context << Instruction::DUP4 << Instruction::SSTORE; - // skip if size is not reduced - _context << Instruction::DUP2 << Instruction::DUP2 - << Instruction::GT << Instruction::ISZERO; - _context.appendConditionalJumpTo(resizeEnd); - - // size reduced, clear the end of the array - // stack: ref new_length old_length - ArrayUtils(_context).convertLengthToSize(_type); - _context << Instruction::DUP2; - ArrayUtils(_context).convertLengthToSize(_type); - // stack: ref new_length old_size new_size - // compute data positions - _context << Instruction::DUP4; - CompilerUtils(_context).computeHashStatic(); - // stack: ref new_length old_size new_size data_pos - _context << Instruction::SWAP2 << Instruction::DUP3 << Instruction::ADD; - // stack: ref new_length data_pos new_size delete_end - _context << Instruction::SWAP2 << Instruction::ADD; - // stack: ref new_length delete_end delete_start - if (_type.storageStride() < 32) - ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256()); - else - ArrayUtils(_context).clearStorageLoop(_type.baseType()); - - _context << resizeEnd; - // cleanup - _context << Instruction::POP << Instruction::POP << Instruction::POP; - solAssert(_context.stackHeight() == stackHeightStart - 2, ""); - } - ); -} - void ArrayUtils::incrementDynamicArraySize(ArrayType const& _type) const { solAssert(_type.location() == DataLocation::Storage, ""); @@ -921,14 +776,14 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const } } -void ArrayUtils::clearStorageLoop(Type const* _type) const +void ArrayUtils::clearStorageLoop(Type const* _type, bool _canOverflow) const { solAssert(_type->storageBytes() >= 32, ""); m_context.callLowLevelFunction( "$clearStorageLoop_" + _type->identifier(), 2, 1, - [_type](CompilerContext& _context) + [_type, _canOverflow](CompilerContext& _context) { unsigned stackHeightStart = _context.stackHeight(); if (_type->category() == Type::Category::Mapping) @@ -943,9 +798,11 @@ void ArrayUtils::clearStorageLoop(Type const* _type) const // check for loop condition _context << Instruction::DUP1 << - Instruction::DUP3 << - Instruction::GT << - Instruction::ISZERO; + Instruction::DUP3; + if (_canOverflow) + _context << Instruction::EQ; + else + _context << Instruction::GT << Instruction::ISZERO; evmasm::AssemblyItem zeroLoopEnd = _context.newTag(); _context.appendConditionalJumpTo(zeroLoopEnd); // delete diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index 326084cb63a8..0aab702e9157 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -61,10 +61,6 @@ class ArrayUtils /// Stack pre: reference (excludes byte offset) /// Stack post: void clearDynamicArray(ArrayType const& _type) const; - /// Changes the size of a dynamic array and clears the tail if it is shortened. - /// Stack pre: reference (excludes byte offset) new_length - /// Stack post: - void resizeDynamicArray(ArrayType const& _type) const; /// Increments the size of a dynamic array by one. /// Does not touch the new data element. In case of a byte array, this might move the /// data. @@ -77,9 +73,10 @@ class ArrayUtils /// Stack post: void popStorageArrayElement(ArrayType const& _type) const; /// Appends a loop that clears a sequence of storage slots of the given type (excluding end). + /// @param _canOverflow whether the storage is treated as circular when clearing. /// Stack pre: end_ref start_ref /// Stack post: end_ref - void clearStorageLoop(Type const* _type) const; + void clearStorageLoop(Type const* _type, bool _canOverflow) const; /// Converts length to size (number of storage slots or calldata/memory bytes). /// if @a _pad then add padding to multiples of 32 bytes for calldata/memory. /// Stack pre: length diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index ffeaa790e1e8..31f9aedd8e29 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1431,7 +1431,7 @@ std::string YulUtilFunctions::cleanUpStorageArrayEndFunction(ArrayType const& _t )") ("convertToSize", arrayConvertLengthToSize(_type)) ("dataPosition", arrayDataAreaFunction(_type)) - ("clearStorageRange", clearStorageRangeFunction(*_type.baseType())) + ("clearStorageRange", clearStorageRangeFunction(*_type.baseType(), !_type.isDynamicallySized())) ("packed", _type.baseType()->storageBytes() <= 16) ("itemsPerSlot", std::to_string(32 / _type.baseType()->storageBytes())) ("storageBytes", std::to_string(_type.baseType()->storageBytes())) @@ -1483,7 +1483,7 @@ std::string YulUtilFunctions::cleanUpDynamicByteArrayEndSlotsFunction(ArrayType )") ("dataLocation", arrayDataAreaFunction(_type)) ("div32Ceil", divide32CeilFunction()) - ("clearStorageRange", clearStorageRangeFunction(*_type.baseType())) + ("clearStorageRange", clearStorageRangeFunction(*_type.baseType(), /* _canOverflow */ false)) .render(); }); } @@ -1523,7 +1523,7 @@ std::string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _ty ("functionName", functionName) ("dataPosition", arrayDataAreaFunction(_type)) ("partialClearStorageSlot", partialClearStorageSlotFunction()) - ("clearStorageRange", clearStorageRangeFunction(*_type.baseType())) + ("clearStorageRange", clearStorageRangeFunction(*_type.baseType(), /* _canOverflow */ true)) ("transitLongToShort", byteArrayTransitLongToShortFunction(_type)) ("div32Ceil", divide32CeilFunction()) ("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction()) @@ -1817,7 +1817,7 @@ std::string YulUtilFunctions::partialClearStorageSlotFunction() }); } -std::string YulUtilFunctions::clearStorageRangeFunction(Type const& _type) +std::string YulUtilFunctions::clearStorageRangeFunction(Type const& _type, bool _canOverflow) { if (_type.storageBytes() < 32) solAssert(_type.isValueType(), ""); @@ -1827,13 +1827,14 @@ std::string YulUtilFunctions::clearStorageRangeFunction(Type const& _type) return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (start, end) { - for {} lt(start, end) { start := add(start, ) } + for {} (start, end) { start := add(start, ) } { (start, 0) } } )") ("functionName", functionName) + ("compare", _canOverflow ? "sub" : "lt") ("setToZero", storageSetToZeroFunction(_type.storageBytes() < 32 ? *TypeProvider::uint256() : _type, VariableDeclaration::Location::Unspecified)) ("increment", _type.storageSize().str()) .render(); @@ -1871,7 +1872,7 @@ std::string YulUtilFunctions::clearStorageArrayFunction(ArrayType const& _type) ( "clearRange", _type.baseType()->category() != Type::Category::Mapping ? - clearStorageRangeFunction((_type.baseType()->storageBytes() < 32) ? *TypeProvider::uint256() : *_type.baseType()) : + clearStorageRangeFunction((_type.baseType()->storageBytes() < 32) ? *TypeProvider::uint256() : *_type.baseType(), /* _canOverflow */ true) : "" ) ("lenToSize", arrayConvertLengthToSize(_type)) diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 5ea99d603083..c62d90b4437a 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -268,7 +268,8 @@ class YulUtilFunctions /// @returns the name of a function that will clear the storage area given /// by the start and end (exclusive) parameters (slots). /// signature: (start, end) - std::string clearStorageRangeFunction(Type const& _type); + /// if _canOverflow is true, it treats the storage as circular and clears by wrapping around. + std::string clearStorageRangeFunction(Type const& _type, bool _canOverflow); /// @returns the name of a function that will clear the given storage array /// signature: (slot) -> diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol index bb621fcc84b5..9329e79098a6 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol @@ -36,11 +36,11 @@ contract c { // ---- // test() -> 0x02000202 // gas irOptimized: 4549676 -// gas legacy: 4475394 -// gas legacyOptimized: 4447665 +// gas legacy: 4473477 +// gas legacyOptimized: 4445748 // storageEmpty -> 1 // clear() -> 0, 0 // gas irOptimized: 4478184 -// gas legacy: 4407185 -// gas legacyOptimized: 4381337 +// gas legacy: 4405274 +// gas legacyOptimized: 4379426 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base_nested.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base_nested.sol index ad745ff4203f..c2ef0d9d46be 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base_nested.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base_nested.sol @@ -21,6 +21,6 @@ contract c { } // ---- // test() -> 3, 4 -// gas irOptimized: 169669 +// gas irOptimized: 169602 // gas legacy: 175415 // gas legacyOptimized: 172533 diff --git a/test/libsolidity/semanticTests/array/push/array_push_struct.sol b/test/libsolidity/semanticTests/array/push/array_push_struct.sol index 92f071e1fb22..29aceb7826a4 100644 --- a/test/libsolidity/semanticTests/array/push/array_push_struct.sol +++ b/test/libsolidity/semanticTests/array/push/array_push_struct.sol @@ -22,4 +22,4 @@ contract c { // test() -> 2, 3, 4, 5 // gas irOptimized: 135329 // gas legacy: 147437 -// gas legacyOptimized: 146429 +// gas legacyOptimized: 148715 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_assignment.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_assignment.sol new file mode 100644 index 000000000000..ac749062df27 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_assignment.sol @@ -0,0 +1,27 @@ +contract C { + function getArray() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function assignArray(uint256[10] memory y) public { + uint256[10][1] storage _x = getArray(); + _x[0] = y; + } + + function x() public view returns (uint256[10] memory) { + return getArray()[0]; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// assignArray(uint256[10]): 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> +// gas irOptimized: 245236 +// gas legacyOptimized: 245441 +// x() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// assignArray(uint256[10]): 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 -> +// x() -> 10, 0x14, 0x1e, 0x28, 0x32, 0x3c, 0x46, 0x50, 0x5a, 0x64 +// gas irOptimized: 198025 +// gas legacy: 200268 +// gas legacyOptimized: 198058 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_delete.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_delete.sol new file mode 100644 index 000000000000..32db82789e48 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_delete.sol @@ -0,0 +1,33 @@ +contract C { + function getArray() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function fillArray() public { + uint256[10][1] storage _x = getArray(); + for (uint i = 1; i < 10; i++) + _x[0][i] = i; + } + + function clearArray() public { + uint256[10][1] storage _x = getArray(); + delete _x[0]; + } + + function x() public view returns (uint256[10] memory) { + return getArray()[0]; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 220705 +// gas legacyOptimized: 220871 +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 +// clearArray() +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 181920 +// gas legacy: 184143 +// gas legacyOptimized: 181899 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_delete_overlapping_variable.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_delete_overlapping_variable.sol new file mode 100644 index 000000000000..53c061caa93d --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_delete_overlapping_variable.sol @@ -0,0 +1,39 @@ +contract C { + uint256 public y = 42; + + function getArray() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function fillArray() public { + uint256[10][1] storage _x = getArray(); + for (uint i = 1; i < 10; i++) + _x[0][i] = i; + } + + function clearArray() public { + uint256[10][1] storage _x = getArray(); + delete _x[0]; + } + + function x() public view returns (uint256[10] memory) { + return getArray()[0]; + } +} + +// ---- +// y() -> 42 +// x() -> 0, 0, 0, 0, 0, 0x2a, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 203627 +// gas legacyOptimized: 203793 +// y() -> 5 +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 +// clearArray() +// y() -> 0 +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 168243 +// gas legacy: 170463 +// gas legacyOptimized: 168219 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_partial_assignment.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_partial_assignment.sol new file mode 100644 index 000000000000..383f7ef304a3 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_partial_assignment.sol @@ -0,0 +1,33 @@ +contract C { + function getArray() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function fillArray() public { + uint256[10][1] storage _x = getArray(); + for (uint i = 1; i < 10; i++) + _x[0][i] = i; + } + + function x() public view returns (uint256[10] memory) { + return getArray()[0]; + } + + function partialAssignArray() public { + uint256[10][1] storage _x = getArray(); + _x[0] = [11, 12, 13]; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 220705 +// gas legacyOptimized: 220871 +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 +// partialAssignArray() +// x() -> 11, 12, 13, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 198025 +// gas legacy: 200268 +// gas legacyOptimized: 198058 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_partial_assignment_cross_border.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_partial_assignment_cross_border.sol new file mode 100644 index 000000000000..17372c9d3f12 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_partial_assignment_cross_border.sol @@ -0,0 +1,33 @@ +contract C { + function getArray() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function fillArray() public { + uint256[10][1] storage _x = getArray(); + for (uint i = 1; i < 10; i++) + _x[0][i] = i; + } + + function x() public view returns (uint256[10] memory) { + return getArray()[0]; + } + + function partialAssignArray() public { + uint256[10][1] storage _x = getArray(); + _x[0] = [11, 12, 13, 14, 15, 16, 17]; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 220705 +// gas legacyOptimized: 220871 +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 +// partialAssignArray() +// x() -> 11, 12, 13, 14, 15, 0x10, 0x11, 0, 0, 0 +// gas irOptimized: 198025 +// gas legacy: 200268 +// gas legacyOptimized: 198058 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_delete_array_with_layout.sol b/test/libsolidity/semanticTests/storage/storage_boundary_delete_array_with_layout.sol new file mode 100644 index 000000000000..6ccf80dd0de0 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_delete_array_with_layout.sol @@ -0,0 +1,52 @@ +contract C layout at 2**256 - 5 { + uint256 a; + + function getArray() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := a.slot + } + } + + function fillArray() public { + uint256[10][1] storage _x = getArray(); + for (uint i = 1; i < 10; i++) + _x[0][i] = i; + } + + function partialAssignArrayBeforeStorageBoundary() public { + uint256[10][1] storage _x = getArray(); + _x[0] = [11, 12, 13]; + } + + function partialAssignArrayCrossStorageBoundary() public { + uint256[10][1] storage _x = getArray(); + _x[0] = [14, 15, 16, 17, 18, 19, 20]; + } + + function clearArray() public { + uint256[10][1] storage _x = getArray(); + delete _x[0]; + } + + function x() public view returns (uint256[10] memory) { + return getArray()[0]; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 220749 +// gas legacyOptimized: 220915 +// partialAssignArrayBeforeStorageBoundary() +// x() -> 11, 12, 13, 0, 0, 0, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 186549 +// gas legacyOptimized: 186715 +// x() -> 11, 1, 2, 3, 4, 5, 6, 7, 8, 9 +// partialAssignArrayCrossStorageBoundary() +// x() -> 14, 15, 0x10, 0x11, 0x12, 0x13, 0x14, 0, 0, 0 +// clearArray() +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 181930 +// gas legacy: 184143 +// gas legacyOptimized: 181900 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_delete_overflow_bug.sol b/test/libsolidity/semanticTests/storage/storage_boundary_delete_overflow_bug.sol new file mode 100644 index 000000000000..5b24a5363a9a --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_delete_overflow_bug.sol @@ -0,0 +1,73 @@ +contract C { + mapping(string => uint256[256][2**240]) m; + + function getSlot() internal view returns (uint256) { + uint256[256][2**240] storage _x = m["v 2.2.3"]; + uint256 slot; + assembly { + slot := _x.slot + } + assert(slot == 0xffdb3f1d9f54eb0b5012935c286c508459d381405d269e01c15f4ec2826edbbf); + return slot; + } + + function getIndex() internal view returns (uint256) { + uint256 slot = getSlot(); + // Pick the largest index such that `slot + 256 * index` <= `2**256 - 1` + uint256 index = (type(uint256).max - slot) / 256; + assert(index <= type(uint240).max); + assert((type(uint256).max - slot + 1) % 256 != 0); + + return index; + } + + function getArray() internal view returns (uint256[256][2**240] storage _x) { + uint256 s = getSlot(); + assembly { + _x.slot := s + } + } + + function fillArray() public { + uint256[256][2**240] storage _x = getArray(); + for (uint i = 1; i < 256; i++) + _x[getIndex()][i] = i; + } + + function partialAssignArray() public { + uint256[256][2**240] storage _x = getArray(); + _x[getIndex()] = [11, 22, 33, 44, 55, 66, 77, 88, 99]; + } + + function clearArray() public { + uint256[256][2**240] storage _x = getArray(); + delete _x[getIndex()]; + } + + function x() public view returns (uint256[256] memory) { + return getArray()[getIndex()]; + } +} + +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 604108 +// gas legacyOptimized: 598016 +// fillArray() +// gas irOptimized: 5782148 +// gas legacyOptimized: 5853893 +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +// gas irOptimized: 604108 +// gas legacyOptimized: 598016 +// partialAssignArray() +// gas irOptimized: 1065037 +// gas legacyOptimized: 1065189 +// x() -> 11, 0x16, 0x21, 0x2c, 0x37, 0x42, 0x4d, 0x58, 0x63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 604108 +// gas legacyOptimized: 598016 +// clearArray() +// gas irOptimized: 578882 +// gas legacyOptimized: 578989 +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 604108 +// gas legacyOptimized: 598016 diff --git a/test/libsolidity/semanticTests/types/mapping/copy_from_mapping_to_mapping.sol b/test/libsolidity/semanticTests/types/mapping/copy_from_mapping_to_mapping.sol index 26e8294e3ac9..cf78fe36cb8b 100644 --- a/test/libsolidity/semanticTests/types/mapping/copy_from_mapping_to_mapping.sol +++ b/test/libsolidity/semanticTests/types/mapping/copy_from_mapping_to_mapping.sol @@ -31,4 +31,4 @@ contract C { // f() -> 0x20, 7, 8, 9, 0xa0, 13, 2, 0x40, 0xa0, 2, 3, 4, 2, 3, 4 // gas irOptimized: 197102 // gas legacy: 199887 -// gas legacyOptimized: 196845 +// gas legacyOptimized: 203694