From b45051daeef4a480c126b295b328f58aa7185a4a Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 11 Mar 2025 22:41:00 +0100 Subject: [PATCH 1/9] Failing test case. --- .../storage/delete_overflow_bug.sol | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 test/libsolidity/semanticTests/storage/delete_overflow_bug.sol diff --git a/test/libsolidity/semanticTests/storage/delete_overflow_bug.sol b/test/libsolidity/semanticTests/storage/delete_overflow_bug.sol new file mode 100644 index 000000000000..02a834b64cfc --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overflow_bug.sol @@ -0,0 +1,17 @@ +contract C { + function getArray() internal returns (uint256[10][1] storage x) { + assembly { + x.slot := sub(0, 1) + } + } + function f() public returns (uint256[10] memory) { + uint256[10][1] storage x = getArray(); + for (uint i = 0; i < 10; i++) + x[0][i] = i; + delete x[0]; + return x[0]; + } +} +// ---- +// f() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 +// gas legacy: 228661 From 27e2e991702d52fae313df830593e80e3096a570 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 11 Mar 2025 22:53:03 +0100 Subject: [PATCH 2/9] Fix storage deletion on overflowing arrays. --- libsolidity/codegen/ArrayUtils.cpp | 3 +-- libsolidity/codegen/YulUtilFunctions.cpp | 2 +- .../semanticTests/storage/delete_overflow_bug.sol | 5 +++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 455cfe0842ed..71f66661f3b5 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -944,8 +944,7 @@ void ArrayUtils::clearStorageLoop(Type const* _type) const _context << Instruction::DUP1 << Instruction::DUP3 << - Instruction::GT << - Instruction::ISZERO; + Instruction::EQ; evmasm::AssemblyItem zeroLoopEnd = _context.newTag(); _context.appendConditionalJumpTo(zeroLoopEnd); // delete diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index ffeaa790e1e8..865a4494e270 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1827,7 +1827,7 @@ 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 {} sub(start, end) { start := add(start, ) } { (start, 0) } diff --git a/test/libsolidity/semanticTests/storage/delete_overflow_bug.sol b/test/libsolidity/semanticTests/storage/delete_overflow_bug.sol index 02a834b64cfc..104e79a7ad37 100644 --- a/test/libsolidity/semanticTests/storage/delete_overflow_bug.sol +++ b/test/libsolidity/semanticTests/storage/delete_overflow_bug.sol @@ -13,5 +13,6 @@ contract C { } } // ---- -// f() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 -// gas legacy: 228661 +// f() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 181920 +// gas legacy: 184143 From bfb14c98287d450cf7fae2f082b2849207e17a50 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 12 Mar 2025 14:41:16 +0100 Subject: [PATCH 3/9] Case distinguish in clearing storage. --- libsolidity/codegen/ArrayUtils.cpp | 29 ++++++++++--------- libsolidity/codegen/ArrayUtils.h | 2 +- libsolidity/codegen/YulUtilFunctions.cpp | 13 +++++---- libsolidity/codegen/YulUtilFunctions.h | 2 +- .../copying/array_copy_including_array.sol | 8 ++--- ..._storage_storage_different_base_nested.sol | 2 +- .../storage/delete_overflow_bug.sol | 1 + 7 files changed, 31 insertions(+), 26 deletions(-) diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 71f66661f3b5..142b51e430a0 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -290,9 +290,9 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons _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()); + utils.clearStorageLoop(TypeProvider::uint256(), false); // TODO: check the boolean else - utils.clearStorageLoop(targetBaseType); + utils.clearStorageLoop(targetBaseType, false); // TODO: check the boolean _context << Instruction::POP; } ); @@ -590,9 +590,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()); // TODO: check boolean else - ArrayUtils(_context).clearStorageLoop(_type.baseType()); + ArrayUtils(_context).clearStorageLoop(_type.baseType(), !_type.isDynamicallySized()); // TODO: check boolean _context << Instruction::POP; } solAssert(_context.stackHeight() == stackHeightStart - 2, ""); @@ -631,9 +631,9 @@ 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(), false); // TODO: check boolean else - clearStorageLoop(_type.baseType()); + clearStorageLoop(_type.baseType(), false); // TODO: check boolean // cleanup m_context << endTag; m_context << Instruction::POP; @@ -738,7 +738,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const 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()); + ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256(), false); _context << Instruction::POP; // stack: ref new_length current_length first_word solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); @@ -777,9 +777,9 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const _context << Instruction::SWAP2 << Instruction::ADD; // stack: ref new_length delete_end delete_start if (_type.storageStride() < 32) - ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256()); + ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256(), false); else - ArrayUtils(_context).clearStorageLoop(_type.baseType()); + ArrayUtils(_context).clearStorageLoop(_type.baseType(), false); _context << resizeEnd; // cleanup @@ -921,14 +921,14 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const } } -void ArrayUtils::clearStorageLoop(Type const* _type) const +void ArrayUtils::clearStorageLoop(Type const* _type, bool _assumeEndAfterStart) const { solAssert(_type->storageBytes() >= 32, ""); m_context.callLowLevelFunction( "$clearStorageLoop_" + _type->identifier(), 2, 1, - [_type](CompilerContext& _context) + [_type, _assumeEndAfterStart](CompilerContext& _context) { unsigned stackHeightStart = _context.stackHeight(); if (_type->category() == Type::Category::Mapping) @@ -943,8 +943,11 @@ void ArrayUtils::clearStorageLoop(Type const* _type) const // check for loop condition _context << Instruction::DUP1 << - Instruction::DUP3 << - Instruction::EQ; + Instruction::DUP3; + if (_assumeEndAfterStart) + _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..8b0339a28531 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -79,7 +79,7 @@ class ArrayUtils /// Appends a loop that clears a sequence of storage slots of the given type (excluding end). /// Stack pre: end_ref start_ref /// Stack post: end_ref - void clearStorageLoop(Type const* _type) const; + void clearStorageLoop(Type const* _type, bool _assumeEndAfterStart) 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 865a4494e270..af6fac419326 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(), false)) ("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(), 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(), 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 _assumeEndAfterStart) { 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 {} sub(start, end) { start := add(start, ) } + for {} (start, end) { start := add(start, ) } { (start, 0) } } )") ("functionName", functionName) + ("compare", _assumeEndAfterStart ? "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(), true) : "" ) ("lenToSize", arrayConvertLengthToSize(_type)) diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 5ea99d603083..7b2019bc4f71 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -268,7 +268,7 @@ 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); + std::string clearStorageRangeFunction(Type const& _type, bool _assumeEndAfterStart); /// @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/storage/delete_overflow_bug.sol b/test/libsolidity/semanticTests/storage/delete_overflow_bug.sol index 104e79a7ad37..63f5bcd70a65 100644 --- a/test/libsolidity/semanticTests/storage/delete_overflow_bug.sol +++ b/test/libsolidity/semanticTests/storage/delete_overflow_bug.sol @@ -16,3 +16,4 @@ contract C { // f() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // gas irOptimized: 181920 // gas legacy: 184143 +// gas legacyOptimized: 181899 From 7ea8d1e6f637968e4720e46342bd4d0cf79c36cf Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Mon, 7 Apr 2025 11:12:32 +0200 Subject: [PATCH 4/9] Add bug list --- docs/bugs.json | 10 +++ docs/bugs_by_version.json | 133 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 136 insertions(+), 7 deletions(-) diff --git a/docs/bugs.json b/docs/bugs.json index 800524669048..68a054dc4903 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,4 +1,14 @@ [ + { + "uid": "SOL-2025-4", + "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", From eaa6ce413cbb2ad4cdd049d746b19a5aff0930b1 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Mon, 7 Apr 2025 12:35:45 +0200 Subject: [PATCH 5/9] Add changelog --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) 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. From 6f75138566dad20153e9909feb620fa21ffa930d Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Fri, 4 Apr 2025 17:56:25 +0200 Subject: [PATCH 6/9] wip --- libsolidity/codegen/ArrayUtils.cpp | 25 ++++++++++++------------ libsolidity/codegen/ArrayUtils.h | 3 ++- libsolidity/codegen/YulUtilFunctions.cpp | 12 ++++++------ libsolidity/codegen/YulUtilFunctions.h | 3 ++- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 142b51e430a0..e636ab3f9cd8 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -290,9 +290,9 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons _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(), false); // TODO: check the boolean + utils.clearStorageLoop(TypeProvider::uint256(), /* _canOverflow */ false); else - utils.clearStorageLoop(targetBaseType, false); // TODO: check the boolean + utils.clearStorageLoop(targetBaseType, /* _canOverflow */ false); _context << Instruction::POP; } ); @@ -590,9 +590,10 @@ 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(), !_type.isDynamicallySized()); // TODO: check boolean + // wraps around cleaning for static arrays + ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256(), !_type.isDynamicallySized()); else - ArrayUtils(_context).clearStorageLoop(_type.baseType(), !_type.isDynamicallySized()); // TODO: check boolean + ArrayUtils(_context).clearStorageLoop(_type.baseType(), !_type.isDynamicallySized()); _context << Instruction::POP; } solAssert(_context.stackHeight() == stackHeightStart - 2, ""); @@ -631,9 +632,9 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const << Instruction::SWAP1; // stack: data_pos_end data_pos if (_type.storageStride() < 32) - clearStorageLoop(TypeProvider::uint256(), false); // TODO: check boolean + clearStorageLoop(TypeProvider::uint256(), /* _canOverflow */ false); else - clearStorageLoop(_type.baseType(), false); // TODO: check boolean + clearStorageLoop(_type.baseType(), /* _canOverflow */ false); // cleanup m_context << endTag; m_context << Instruction::POP; @@ -738,7 +739,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const 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(), false); + ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256(), /* _canOverflow */ false); _context << Instruction::POP; // stack: ref new_length current_length first_word solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); @@ -777,9 +778,9 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const _context << Instruction::SWAP2 << Instruction::ADD; // stack: ref new_length delete_end delete_start if (_type.storageStride() < 32) - ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256(), false); + ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256(), /* _canOverflow */ false); else - ArrayUtils(_context).clearStorageLoop(_type.baseType(), false); + ArrayUtils(_context).clearStorageLoop(_type.baseType(), /* _canOverflow */ false); _context << resizeEnd; // cleanup @@ -921,14 +922,14 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const } } -void ArrayUtils::clearStorageLoop(Type const* _type, bool _assumeEndAfterStart) const +void ArrayUtils::clearStorageLoop(Type const* _type, bool _canOverflow) const { solAssert(_type->storageBytes() >= 32, ""); m_context.callLowLevelFunction( "$clearStorageLoop_" + _type->identifier(), 2, 1, - [_type, _assumeEndAfterStart](CompilerContext& _context) + [_type, _canOverflow](CompilerContext& _context) { unsigned stackHeightStart = _context.stackHeight(); if (_type->category() == Type::Category::Mapping) @@ -944,7 +945,7 @@ void ArrayUtils::clearStorageLoop(Type const* _type, bool _assumeEndAfterStart) _context << Instruction::DUP1 << Instruction::DUP3; - if (_assumeEndAfterStart) + if (_canOverflow) _context << Instruction::EQ; else _context << Instruction::GT << Instruction::ISZERO; diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index 8b0339a28531..6fc8f509b45b 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -77,9 +77,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, bool _assumeEndAfterStart) 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 af6fac419326..04ae5c3dc0db 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(), false)) + ("clearStorageRange", clearStorageRangeFunction(*_type.baseType(), /* _canOverflow */ false)) ("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(), false)) + ("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(), true)) + ("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, bool _assumeEndAfterStart) +std::string YulUtilFunctions::clearStorageRangeFunction(Type const& _type, bool _canOverflow) { if (_type.storageBytes() < 32) solAssert(_type.isValueType(), ""); @@ -1834,7 +1834,7 @@ std::string YulUtilFunctions::clearStorageRangeFunction(Type const& _type, bool } )") ("functionName", functionName) - ("compare", _assumeEndAfterStart ? "sub" : "lt") + ("compare", _canOverflow ? "sub" : "lt") ("setToZero", storageSetToZeroFunction(_type.storageBytes() < 32 ? *TypeProvider::uint256() : _type, VariableDeclaration::Location::Unspecified)) ("increment", _type.storageSize().str()) .render(); @@ -1872,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(), true) : + 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 7b2019bc4f71..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, bool _assumeEndAfterStart); + /// 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) -> From f6e116aaeca7639c6e15a89a29b669d26b8a6fc2 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:48:30 +0200 Subject: [PATCH 7/9] Add tests --- .../storage/delete_overflow_bug_collision.sol | 29 +++++++++++++++++++ ...low_bug_large_mapping_storage_boundary.sol | 24 +++++++++++++++ ...delete_overflow_bug_partial_assignment.sol | 19 ++++++++++++ .../delete_overflow_bug_storage_layout.sol | 22 ++++++++++++++ 4 files changed, 94 insertions(+) create mode 100644 test/libsolidity/semanticTests/storage/delete_overflow_bug_collision.sol create mode 100644 test/libsolidity/semanticTests/storage/delete_overflow_bug_large_mapping_storage_boundary.sol create mode 100644 test/libsolidity/semanticTests/storage/delete_overflow_bug_partial_assignment.sol create mode 100644 test/libsolidity/semanticTests/storage/delete_overflow_bug_storage_layout.sol diff --git a/test/libsolidity/semanticTests/storage/delete_overflow_bug_collision.sol b/test/libsolidity/semanticTests/storage/delete_overflow_bug_collision.sol new file mode 100644 index 000000000000..1cf7641167eb --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overflow_bug_collision.sol @@ -0,0 +1,29 @@ +contract C { + uint256 y = 42; + + function getArray() internal returns (uint256[10][1] storage x) { + assembly { + x.slot := sub(0, 1) + } + } + + function f() public returns (uint256[10] memory) { + uint256[10][1] storage x = getArray(); + for (uint i = 0; i < 10; i++) + x[0][i] = i; + delete x[0]; + return x[0]; + } + + function g() public view returns (uint256) { + return y; + } +} + +// ---- +// g() -> 42 +// f() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 168243 +// gas legacy: 170463 +// gas legacyOptimized: 168219 +// g() -> 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overflow_bug_large_mapping_storage_boundary.sol b/test/libsolidity/semanticTests/storage/delete_overflow_bug_large_mapping_storage_boundary.sol new file mode 100644 index 000000000000..922f552eafe6 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overflow_bug_large_mapping_storage_boundary.sol @@ -0,0 +1,24 @@ +contract C { + mapping(string => uint256[256][2**240]) m; + + function f() public returns (uint256[256] memory) { + uint256[256][2**240] storage a = m["v 2.2.3"]; // storage slot at: 115727151504557284028719796883655996022159813646012550228975965253679287557055 + uint256 slot; + assembly { + slot := a.slot + } + + // Pick the largest k such that `slot + 256 * k` <= `2**256 - 1` + uint240 k = uint240((type(uint256).max - slot) / 256); + require(k <= type(uint240).max); + + a[k] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + delete a[k]; + return a[k]; + } +} + +// ---- +// f() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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: 723216 +// gas legacyOptimized: 717881 diff --git a/test/libsolidity/semanticTests/storage/delete_overflow_bug_partial_assignment.sol b/test/libsolidity/semanticTests/storage/delete_overflow_bug_partial_assignment.sol new file mode 100644 index 000000000000..3ff713c4d605 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overflow_bug_partial_assignment.sol @@ -0,0 +1,19 @@ +contract C { + function getArray() internal returns (uint256[10][1] storage x) { + assembly { + x.slot := sub(0, 1) + } + } + function f() public returns (uint256[10] memory) { + uint256[10][1] storage x = getArray(); + for (uint i = 0; i < 10; i++) + x[0][i] = i; + x[0] = [11, 12, 13]; + return x[0]; + } +} +// ---- +// f() -> 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/delete_overflow_bug_storage_layout.sol b/test/libsolidity/semanticTests/storage/delete_overflow_bug_storage_layout.sol new file mode 100644 index 000000000000..054243eceb59 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overflow_bug_storage_layout.sol @@ -0,0 +1,22 @@ +contract C layout at 2**256 - 2 { + uint256 a; + + function getArray() internal returns (uint256[10][1] storage x) { + assembly { + x.slot := a.slot + } + } + + function f() public returns (uint256[10] memory) { + uint256[10][1] storage x = getArray(); + for (uint i = 0; i < 10; i++) + x[0][i] = i; + delete x[0]; + return x[0]; + } +} +// ---- +// f() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 181930 +// gas legacy: 184143 +// gas legacyOptimized: 181900 From 0d846915f9d1a59e8989f0bee9f6f935b38f39b1 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Mon, 14 Apr 2025 17:30:57 +0200 Subject: [PATCH 8/9] Remove unused --- libsolidity/codegen/ArrayUtils.cpp | 150 ----------------------------- libsolidity/codegen/ArrayUtils.h | 4 - 2 files changed, 154 deletions(-) diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index e636ab3f9cd8..6590e3cfb02e 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -640,156 +640,6 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const 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(), /* _canOverflow */ false); - _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(), /* _canOverflow */ false); - else - ArrayUtils(_context).clearStorageLoop(_type.baseType(), /* _canOverflow */ false); - - _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, ""); diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index 6fc8f509b45b..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. From 0d1f552aa6af4eb61f98c854e3838dbee25a23ff Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Wed, 16 Apr 2025 12:46:36 +0200 Subject: [PATCH 9/9] wip --- libsolidity/codegen/ArrayUtils.cpp | 12 ++++++++---- libsolidity/codegen/YulUtilFunctions.cpp | 2 +- .../semanticTests/array/push/array_push_struct.sol | 2 +- .../types/mapping/copy_from_mapping_to_mapping.sol | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 6590e3cfb02e..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(), /* _canOverflow */ false); + if (_targetType.isDynamicallySized()) + ArrayUtils(_context).clearDynamicArray(_targetType); else - utils.clearStorageLoop(targetBaseType, /* _canOverflow */ false); + { + if (targetBaseType->storageBytes() < 32) + utils.clearStorageLoop(TypeProvider::uint256(), /* _canOverflow */ true); + else + utils.clearStorageLoop(targetBaseType, /* _canOverflow */ true); + } _context << Instruction::POP; } ); @@ -590,7 +595,6 @@ void ArrayUtils::clearArray(ArrayType const& _typeIn) const ArrayUtils(_context).convertLengthToSize(_type); _context << Instruction::ADD << Instruction::SWAP1; if (_type.baseType()->storageBytes() < 32) - // wraps around cleaning for static arrays ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256(), !_type.isDynamicallySized()); else ArrayUtils(_context).clearStorageLoop(_type.baseType(), !_type.isDynamicallySized()); diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 04ae5c3dc0db..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(), /* _canOverflow */ false)) + ("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())) 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/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