From 1364407e6b94f6acd676cf215487d8695df76e05 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Thu, 30 Apr 2026 20:24:24 -0300 Subject: [PATCH 1/3] Fix incorrect retrieval of storage packed internal function pointers --- Changelog.md | 1 + libsolidity/codegen/LValue.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index 1d11888028ad..c033e1b13675 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,7 @@ Compiler Features: Bugfixes: * Code Generator: Fix ICE on parenthesized custom error construction in require statement. +* Code Generator: Fix uninitialized internal function pointers being read from a packed storage slot with the wrong value when a subsequent variable in the slot holds a non-zero value. * Commandline Interface: Report proper error instead of ICE on non-hex mixed-case address value given via `--libraries`. Build System: diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index e8f6ae61c65b..cd751f77b91c 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -242,6 +242,7 @@ void GenericStorageItem::retrieveValue(langutil::SourceLocation con if (type->category() == Type::Category::UserDefinedValueType) type = type->encodingType(); bool cleaned = false; + // shift bits to the right positioning the state variable at the start of the slot m_context << Instruction::SWAP1 << s_loadInstruction << Instruction::SWAP1 << u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV; @@ -257,9 +258,12 @@ void GenericStorageItem::retrieveValue(langutil::SourceLocation con } else if (fun->kind() == FunctionType::Kind::Internal) { + // internal function pointers occupy 8 bytes, so we mask the remaining bytes in the slot + m_context << ((u256(0x1) << (8 * type->storageBytes())) - 1) << Instruction::AND; m_context << Instruction::DUP1 << Instruction::ISZERO; CompilerUtils(m_context).pushZeroValue(*fun); m_context << Instruction::MUL << Instruction::OR; + cleaned = true; } } else if (type->leftAligned()) From c2aacc79c8dffe99d0e62d8072ce01a054817df1 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Thu, 6 Aug 2026 23:10:00 -0300 Subject: [PATCH 2/3] fixup! update tests --- .../optimizer_inliner_dynamic_reference/output | 13 +++++++++---- .../output | 13 +++++++++---- ...all_to_zero_initialized_function_type_legacy.sol | 6 +++++- .../semanticTests/functionTypes/store_function.sol | 2 +- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/test/cmdlineTests/optimizer_inliner_dynamic_reference/output b/test/cmdlineTests/optimizer_inliner_dynamic_reference/output index 5313893fc735..35877fd1270e 100644 --- a/test/cmdlineTests/optimizer_inliner_dynamic_reference/output +++ b/test/cmdlineTests/optimizer_inliner_dynamic_reference/output @@ -112,15 +112,20 @@ sub_0: assembly { /* "input.sol":295:298 x() */ tag_19 swap1 + 0xffffffff /* "input.sol":295:296 x */ - dup1 - iszero tag_20 + 0xffffffffffffffff + dup4 + and + iszero mul - or /* "input.sol":295:298 x() */ - 0xffffffff + dup2 and + swap2 + and + or jump // in tag_19: /* "input.sol":295:302 x() + 1 */ diff --git a/test/cmdlineTests/optimizer_inliner_dynamic_reference_constructor/output b/test/cmdlineTests/optimizer_inliner_dynamic_reference_constructor/output index 5997bd1c7b60..1cf48a0d6962 100644 --- a/test/cmdlineTests/optimizer_inliner_dynamic_reference_constructor/output +++ b/test/cmdlineTests/optimizer_inliner_dynamic_reference_constructor/output @@ -117,15 +117,20 @@ sub_0: assembly { /* "input.sol":289:292 x() */ tag_16 swap1 + 0xffffffff /* "input.sol":289:290 x */ - dup1 - iszero tag_17 + 0xffffffffffffffff + dup4 + and + iszero mul - or /* "input.sol":289:292 x() */ - 0xffffffff + dup2 and + swap2 + and + or jump // in tag_16: /* "input.sol":289:296 x() + 1 */ diff --git a/test/libsolidity/semanticTests/functionTypes/call_to_zero_initialized_function_type_legacy.sol b/test/libsolidity/semanticTests/functionTypes/call_to_zero_initialized_function_type_legacy.sol index feca44e0c10f..f11ad4228403 100644 --- a/test/libsolidity/semanticTests/functionTypes/call_to_zero_initialized_function_type_legacy.sol +++ b/test/libsolidity/semanticTests/functionTypes/call_to_zero_initialized_function_type_legacy.sol @@ -24,4 +24,8 @@ contract C { // ==== // compileViaYul: false // ---- -// t() -> FAILURE +// t() -> FAILURE, hex"4e487b71", 0x51 +// gas legacy: 77534 +// gas legacy code: 69600 +// gas legacyOptimized: 76677 +// gas legacyOptimized code: 28600 diff --git a/test/libsolidity/semanticTests/functionTypes/store_function.sol b/test/libsolidity/semanticTests/functionTypes/store_function.sol index 904bc705f725..d95912ef5989 100644 --- a/test/libsolidity/semanticTests/functionTypes/store_function.sol +++ b/test/libsolidity/semanticTests/functionTypes/store_function.sol @@ -29,5 +29,5 @@ contract C { // gas irOptimized code: 19000 // gas legacy: 79492 // gas legacy code: 69600 -// gas legacyOptimized: 77587 +// gas legacyOptimized: 77611 // gas legacyOptimized code: 28600 From 7f82e49306814c00186e5955e4066ffdc10a3537 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Thu, 6 Aug 2026 23:12:00 -0300 Subject: [PATCH 3/3] Add tests for uninitialized internal function pointer comparisons --- ...al_storage_function_pointer_comparison.sol | 22 +++++++++++++++ ...nt_storage_function_pointer_comparison.sol | 28 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 test/libsolidity/semanticTests/functionTypes/uninitialized_internal_storage_function_pointer_comparison.sol create mode 100644 test/libsolidity/semanticTests/functionTypes/uninitialized_internal_transient_storage_function_pointer_comparison.sol diff --git a/test/libsolidity/semanticTests/functionTypes/uninitialized_internal_storage_function_pointer_comparison.sol b/test/libsolidity/semanticTests/functionTypes/uninitialized_internal_storage_function_pointer_comparison.sol new file mode 100644 index 000000000000..dc19d9e624c0 --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/uninitialized_internal_storage_function_pointer_comparison.sol @@ -0,0 +1,22 @@ +contract C { + uint32 public x = 1; + function() internal internalFunctionPointer; + uint32 private y = 2; + + function testComparison() public view returns (bool) { + function() internal uninitializedPointer; + if (internalFunctionPointer == uninitializedPointer) + return true; + return false; + } + function testLocalAssignment() public view returns (bool) { + function() internal uninitializedPointer; + function() internal localPointer = internalFunctionPointer; + if (localPointer == uninitializedPointer) + return true; + return false; + } +} +// ---- +// testComparison() -> true +// testLocalAssignment() -> true diff --git a/test/libsolidity/semanticTests/functionTypes/uninitialized_internal_transient_storage_function_pointer_comparison.sol b/test/libsolidity/semanticTests/functionTypes/uninitialized_internal_transient_storage_function_pointer_comparison.sol new file mode 100644 index 000000000000..23cf90ef97d2 --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/uninitialized_internal_transient_storage_function_pointer_comparison.sol @@ -0,0 +1,28 @@ +contract C { + uint32 public transient x; + function() internal transient internalFunctionPointer; + uint32 private transient y; + + function testComparison() public returns (bool) { + x = 1; + y = 2; + function() internal uninitializedPointer; + if (internalFunctionPointer == uninitializedPointer) + return true; + return false; + } + function testLocalAssignment() public returns (bool) { + x = 1; + y = 2; + function() internal uninitializedPointer; + function() internal localPointer = internalFunctionPointer; + if (localPointer == uninitializedPointer) + return true; + return false; + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// testComparison() -> true +// testLocalAssignment() -> true