From e89efe89a68cd27a2b75258c54405d74ae16ac45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Acosta?= Date: Wed, 1 Feb 2023 15:49:34 -0300 Subject: [PATCH 001/228] style(mapping-types): fix mapping style --- docs/types/mapping-types.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/types/mapping-types.rst b/docs/types/mapping-types.rst index 68f963753ea1..454caaf05d6e 100644 --- a/docs/types/mapping-types.rst +++ b/docs/types/mapping-types.rst @@ -95,8 +95,8 @@ The example below uses ``_allowances`` to record the amount someone else is allo contract MappingExample { - mapping (address => uint256) private _balances; - mapping (address => mapping (address => uint256)) private _allowances; + mapping(address => uint256) private _balances; + mapping(address => mapping(address => uint256)) private _allowances; event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); From 4ff310cc625c4406884d63999b691da0e554e9e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Acosta?= Date: Wed, 1 Feb 2023 15:50:48 -0300 Subject: [PATCH 002/228] style(common-patterns): fix mapping style --- docs/common-patterns.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 1793ebd2f27a..766f8f35ed58 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -34,7 +34,7 @@ you receive the funds of the person who is now the richest. address public richest; uint public mostSent; - mapping (address => uint) pendingWithdrawals; + mapping(address => uint) pendingWithdrawals; /// The amount of Ether sent was not higher than /// the currently highest amount. From 7b8478a81b86f2f8ccae4219d5542d529227fd5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Acosta?= Date: Wed, 1 Feb 2023 15:51:26 -0300 Subject: [PATCH 003/228] style(intro-sc): fix mapping style --- docs/introduction-to-smart-contracts.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 5a278ecfb107..146f2224beda 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -91,7 +91,7 @@ registering with a username and password, all you need is an Ethereum keypair. // The keyword "public" makes variables // accessible from other contracts address public minter; - mapping (address => uint) public balances; + mapping(address => uint) public balances; // Events allow clients to react to specific // contract changes you declare @@ -151,7 +151,7 @@ You do not need to do this, the compiler figures it out for you. .. index:: mapping -The next line, ``mapping (address => uint) public balances;`` also +The next line, ``mapping(address => uint) public balances;`` also creates a public state variable, but it is a more complex datatype. The :ref:`mapping ` type maps addresses to :ref:`unsigned integers `. From 1edfd73b377040bfd0aef0292624652e6f0c8862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Acosta?= Date: Wed, 1 Feb 2023 15:51:56 -0300 Subject: [PATCH 004/228] style(security-considerations): fix mapping style --- docs/security-considerations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index 03754843163c..0a86a8d5985d 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -336,7 +336,7 @@ field of a ``struct`` that is the base type of a dynamic storage array. The pragma solidity >=0.6.0 <0.9.0; contract Map { - mapping (uint => uint)[] array; + mapping(uint => uint)[] array; function allocate(uint newMaps) public { for (uint i = 0; i < newMaps; i++) From 72a17ceb71a8eed590578b6f788a8cbe67136bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Acosta?= Date: Wed, 1 Feb 2023 15:52:34 -0300 Subject: [PATCH 005/228] style(function-modifiers): fix mapping style --- docs/contracts/function-modifiers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts/function-modifiers.rst b/docs/contracts/function-modifiers.rst index bbdec1e4886c..b6b83bf3ccd0 100644 --- a/docs/contracts/function-modifiers.rst +++ b/docs/contracts/function-modifiers.rst @@ -61,7 +61,7 @@ if they are marked ``virtual``. For details, please see } contract Register is priced, destructible { - mapping (address => bool) registeredAddresses; + mapping(address => bool) registeredAddresses; uint price; constructor(uint initialPrice) { price = initialPrice; } From 5b149adfcd9bfba59f2adcf6f1160558b4694005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Acosta?= Date: Wed, 1 Feb 2023 15:53:04 -0300 Subject: [PATCH 006/228] style(visibility-and-getters): fix mapping style --- docs/contracts/visibility-and-getters.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contracts/visibility-and-getters.rst b/docs/contracts/visibility-and-getters.rst index 8932c5079b03..5bf46dea551b 100644 --- a/docs/contracts/visibility-and-getters.rst +++ b/docs/contracts/visibility-and-getters.rst @@ -200,12 +200,12 @@ The next example is more complex: struct Data { uint a; bytes3 b; - mapping (uint => uint) map; + mapping(uint => uint) map; uint[3] c; uint[] d; bytes e; } - mapping (uint => mapping(bool => Data[])) public data; + mapping(uint => mapping(bool => Data[])) public data; } It generates a function of the following form. The mapping and arrays (with the From 9b0556caa4d5476d92262eae5ae01df4f8afcff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Acosta?= Date: Wed, 1 Feb 2023 15:53:33 -0300 Subject: [PATCH 007/228] style(modular): fix mapping style --- docs/examples/modular.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/modular.rst b/docs/examples/modular.rst index 697699ae6e13..f96b296df727 100644 --- a/docs/examples/modular.rst +++ b/docs/examples/modular.rst @@ -34,7 +34,7 @@ and the sum of all balances is an invariant across the lifetime of the contract. contract Token { mapping(address => uint256) balances; using Balances for *; - mapping(address => mapping (address => uint256)) allowed; + mapping(address => mapping(address => uint256)) allowed; event Transfer(address from, address to, uint amount); event Approval(address owner, address spender, uint amount); From b403085fa15c5d35ed94b33d44c2b5abeabfb575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Acosta?= Date: Wed, 1 Feb 2023 15:54:08 -0300 Subject: [PATCH 008/228] style(layout_in_storage): fix mapping style --- docs/internals/layout_in_storage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/internals/layout_in_storage.rst b/docs/internals/layout_in_storage.rst index 53670eeb853f..a492dea14c88 100644 --- a/docs/internals/layout_in_storage.rst +++ b/docs/internals/layout_in_storage.rst @@ -232,7 +232,7 @@ value and reference types, types that are encoded packed, and nested types. uint y; S s; address addr; - mapping (uint => mapping (address => bool)) map; + mapping(uint => mapping(address => bool)) map; uint[] array; string s1; bytes b1; From da7dfeb0c52e706859fdbe520c5d2a2506ca9cff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Acosta?= Date: Wed, 1 Feb 2023 15:54:32 -0300 Subject: [PATCH 009/228] style(reference-types): fix mapping style --- docs/types/reference-types.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index 6a7667b6b955..771fdecee892 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -686,11 +686,11 @@ shown in the following example: uint fundingGoal; uint numFunders; uint amount; - mapping (uint => Funder) funders; + mapping(uint => Funder) funders; } uint numCampaigns; - mapping (uint => Campaign) campaigns; + mapping(uint => Campaign) campaigns; function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) { campaignID = numCampaigns++; // campaignID is return variable From e735ff1a95497160840afa772dab2a32181db8bc Mon Sep 17 00:00:00 2001 From: "Rodrigo Q. Saramago" Date: Wed, 1 Feb 2023 20:24:50 +0100 Subject: [PATCH 010/228] Set version to 0.8.19 --- CMakeLists.txt | 2 +- Changelog.md | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d04d708ae25a..cefaeeb656ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.8.18") +set(PROJECT_VERSION "0.8.19") # OSX target needed in order to support std::visit set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) diff --git a/Changelog.md b/Changelog.md index 6b804fbd6c4a..8a3bfae6a896 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,14 @@ +### 0.8.19 (unreleased) + +Language Features: + + +Compiler Features: + + +Bugfixes: + + ### 0.8.18 (2023-02-01) Language Features: From f8880cad820f741a5288ed42a6719ab6aef6424b Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Wed, 30 Mar 2022 11:15:04 +0200 Subject: [PATCH 011/228] Yul grammar generator: Bound memory accesses. --- .../libyul/yulInterpreterTests/and_create.yul | 2 - .../yulInterpreterTests/and_create2.yul | 2 - .../external_call_to_self.yul | 1 - .../infinite_recursion_tracelimit.yul | 34 +---- .../yulInterpreterTests/pop_byte_shr_call.yul | 1 - .../libyul/yulInterpreterTests/zero_range.yul | 1 - test/tools/ossfuzz/protoToYul.cpp | 99 +++++++++--- test/tools/ossfuzz/protoToYul.h | 5 + .../EVMInstructionInterpreter.cpp | 142 +++++++++++++++--- .../EVMInstructionInterpreter.h | 13 ++ 10 files changed, 215 insertions(+), 85 deletions(-) diff --git a/test/libyul/yulInterpreterTests/and_create.yul b/test/libyul/yulInterpreterTests/and_create.yul index 8907081506bf..cbc68ca623f1 100644 --- a/test/libyul/yulInterpreterTests/and_create.yul +++ b/test/libyul/yulInterpreterTests/and_create.yul @@ -6,8 +6,6 @@ } // ---- // Trace: -// CREATE(0, 0xffffffffffffffffffffffffffffffffffffffff, 0) -// CREATE(0, 0xffffffffffffffffffffffffffffffffffffffff, 0) // Memory dump: // 0: 0000000000000000000000000000000000000000000000000000000000000001 // Storage dump: diff --git a/test/libyul/yulInterpreterTests/and_create2.yul b/test/libyul/yulInterpreterTests/and_create2.yul index 46df2a9caece..0c0135500dd3 100644 --- a/test/libyul/yulInterpreterTests/and_create2.yul +++ b/test/libyul/yulInterpreterTests/and_create2.yul @@ -8,8 +8,6 @@ // EVMVersion: >=constantinople // ---- // Trace: -// CREATE2(0, 0xffffffffffffffffffffffffffffffffffffffff, 0, 0) -// CREATE2(0, 0xffffffffffffffffffffffffffffffffffffffff, 0, 0) // Memory dump: // 0: 0000000000000000000000000000000000000000000000000000000000000001 // Storage dump: diff --git a/test/libyul/yulInterpreterTests/external_call_to_self.yul b/test/libyul/yulInterpreterTests/external_call_to_self.yul index ad2d2bb77f7d..711159a9fc54 100644 --- a/test/libyul/yulInterpreterTests/external_call_to_self.yul +++ b/test/libyul/yulInterpreterTests/external_call_to_self.yul @@ -14,7 +14,6 @@ // ---- // Trace: // CALL(153, 0x11111111, 0, 64, 32, 256, 32) -// RETURN(0, 0) // Memory dump: // 40: 0000000000000000000000000000000000000000000000000000000000000042 // 100: 0000000000000000000000000000000000000000000000000000000000000042 diff --git a/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul b/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul index 45d3cc781c71..65c28ed5e7cf 100644 --- a/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul +++ b/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul @@ -7,38 +7,6 @@ } // ---- // Trace: -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// LOG0(0, 0) -// Trace size limit reached. +// Interpreter execution step limit reached. // Memory dump: // Storage dump: diff --git a/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul b/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul index 4e431a6b182e..51e0602a3bc8 100644 --- a/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul +++ b/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul @@ -5,6 +5,5 @@ // EVMVersion: >=constantinople // ---- // Trace: -// CALL(0, 0, 0, 0, 0, 0, 0) // Memory dump: // Storage dump: diff --git a/test/libyul/yulInterpreterTests/zero_range.yul b/test/libyul/yulInterpreterTests/zero_range.yul index b39a10feb370..5e1042dfbcd6 100644 --- a/test/libyul/yulInterpreterTests/zero_range.yul +++ b/test/libyul/yulInterpreterTests/zero_range.yul @@ -5,6 +5,5 @@ } // ---- // Trace: -// CALLDATACOPY(32, 0, 0) // Memory dump: // Storage dump: diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index 991b25e75259..25b5a977df84 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -351,9 +351,22 @@ void ProtoConverter::visit(BinaryOp const& _x) break; } m_output << "("; - visit(_x.left()); - m_output << ","; - visit(_x.right()); + if (op == BinaryOp::KECCAK) + { + m_output << "mod("; + visit(_x.left()); + m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")"; + m_output << ","; + m_output << "mod("; + visit(_x.right()); + m_output << ", " << to_string(s_maxSize) << ")"; + } + else + { + visit(_x.left()); + m_output << ","; + visit(_x.right()); + } m_output << ")"; } @@ -623,7 +636,14 @@ void ProtoConverter::visit(UnaryOp const& _x) break; } m_output << "("; - visit(_x.operand()); + if (op == UnaryOp::MLOAD) + { + m_output << "mod("; + visit(_x.operand()); + m_output << ", " << to_string(s_maxMemory) << ")"; + } + else + visit(_x.operand()); m_output << ")"; } @@ -778,11 +798,15 @@ void ProtoConverter::visit(CopyFunc const& _x) break; } m_output << "("; + m_output << "mod("; visit(_x.target()); + m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")"; m_output << ", "; visit(_x.source()); m_output << ", "; + m_output << "mod("; visit(_x.size()); + m_output << ", " << to_string(s_maxSize) << ")"; m_output << ")\n"; } @@ -792,32 +816,42 @@ void ProtoConverter::visit(ExtCodeCopy const& _x) m_output << "("; visit(_x.addr()); m_output << ", "; + m_output << "mod("; visit(_x.target()); + m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")"; m_output << ", "; visit(_x.source()); m_output << ", "; + m_output << "mod("; visit(_x.size()); + m_output << ", " << to_string(s_maxSize) << ")"; m_output << ")\n"; } void ProtoConverter::visit(LogFunc const& _x) { + auto visitPosAndSize = [&](LogFunc const& _y) { + m_output << "mod("; + visit(_y.pos()); + m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")"; + m_output << ", "; + m_output << "mod("; + visit(_y.size()); + m_output << ", " << to_string(s_maxSize) << ")"; + }; + switch (_x.num_topics()) { case LogFunc::ZERO: m_output << "log0"; m_output << "("; - visit(_x.pos()); - m_output << ", "; - visit(_x.size()); + visitPosAndSize(_x); m_output << ")\n"; break; case LogFunc::ONE: m_output << "log1"; m_output << "("; - visit(_x.pos()); - m_output << ", "; - visit(_x.size()); + visitPosAndSize(_x); m_output << ", "; visit(_x.t1()); m_output << ")\n"; @@ -825,9 +859,7 @@ void ProtoConverter::visit(LogFunc const& _x) case LogFunc::TWO: m_output << "log2"; m_output << "("; - visit(_x.pos()); - m_output << ", "; - visit(_x.size()); + visitPosAndSize(_x); m_output << ", "; visit(_x.t1()); m_output << ", "; @@ -837,9 +869,7 @@ void ProtoConverter::visit(LogFunc const& _x) case LogFunc::THREE: m_output << "log3"; m_output << "("; - visit(_x.pos()); - m_output << ", "; - visit(_x.size()); + visitPosAndSize(_x); m_output << ", "; visit(_x.t1()); m_output << ", "; @@ -851,9 +881,7 @@ void ProtoConverter::visit(LogFunc const& _x) case LogFunc::FOUR: m_output << "log4"; m_output << "("; - visit(_x.pos()); - m_output << ", "; - visit(_x.size()); + visitPosAndSize(_x); m_output << ", "; visit(_x.t1()); m_output << ", "; @@ -1015,13 +1043,21 @@ void ProtoConverter::visit(LowLevelCall const& _x) visit(_x.wei()); m_output << ", "; } + m_output << "mod("; visit(_x.in()); + m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")"; m_output << ", "; + m_output << "mod("; visit(_x.insize()); + m_output << ", " << to_string(s_maxSize) << ")"; m_output << ", "; + m_output << "mod("; visit(_x.out()); + m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")"; m_output << ", "; + m_output << "mod("; visit(_x.outsize()); + m_output << ", " << to_string(s_maxSize) << ")"; m_output << ")"; } @@ -1048,9 +1084,13 @@ void ProtoConverter::visit(Create const& _x) } visit(_x.wei()); m_output << ", "; + m_output << "mod("; visit(_x.position()); + m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")"; m_output << ", "; + m_output << "mod("; visit(_x.size()); + m_output << ", " << to_string(s_maxSize) << ")"; if (type == Create::CREATE2) { m_output << ", "; @@ -1069,7 +1109,8 @@ void ProtoConverter::visit(IfStmt const& _x) void ProtoConverter::visit(StoreFunc const& _x) { - switch (_x.st()) + auto storeType = _x.st(); + switch (storeType) { case StoreFunc::MSTORE: m_output << "mstore("; @@ -1081,7 +1122,15 @@ void ProtoConverter::visit(StoreFunc const& _x) m_output << "mstore8("; break; } - visit(_x.loc()); + // Write to memory within bounds, storage is unbounded + if (storeType == StoreFunc::SSTORE) + visit(_x.loc()); + else + { + m_output << "mod("; + visit(_x.loc()); + m_output << ", " << to_string(s_maxMemory) << ")"; + } m_output << ", "; visit(_x.val()); m_output << ")\n"; @@ -1262,9 +1311,13 @@ void ProtoConverter::visit(RetRevStmt const& _x) break; } m_output << "("; + m_output << "mod("; visit(_x.pos()); + m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")"; m_output << ", "; + m_output << "mod("; visit(_x.size()); + m_output << ", " << to_string(s_maxSize) << ")"; m_output << ")\n"; } @@ -1651,8 +1704,12 @@ void ProtoConverter::fillFunctionCallInput(unsigned _numInParams) m_output << "calldataload(" << slot << ")"; break; case 1: + { + // Access memory within stipulated bounds + slot = "mod(" + dictionaryToken() + ", " + to_string(s_maxMemory) + ")"; m_output << "mload(" << slot << ")"; break; + } case 2: m_output << "sload(" << slot << ")"; break; diff --git a/test/tools/ossfuzz/protoToYul.h b/test/tools/ossfuzz/protoToYul.h index 9100b490ce35..00559fa93aa6 100644 --- a/test/tools/ossfuzz/protoToYul.h +++ b/test/tools/ossfuzz/protoToYul.h @@ -344,6 +344,11 @@ class ProtoConverter static unsigned constexpr s_modOutputParams = 5; /// Hard-coded identifier for a Yul object's data block static auto constexpr s_dataIdentifier = "datablock"; + /// Upper bound on memory writes = 2**32 - 1 + /// See: https://eips.ethereum.org/EIPS/eip-1985#memory-size + static unsigned constexpr s_maxMemory = 4294967295; + /// Upper bound on size for range copy functions + static unsigned constexpr s_maxSize = 65536; /// Predicate to keep track of for body scope. If false, break/continue /// statements can not be created. bool m_inForBodyScope; diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index db0e6482867f..ed73a43813f3 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -211,22 +211,24 @@ u256 EVMInstructionInterpreter::eval( case Instruction::CALLDATASIZE: return m_state.calldata.size(); case Instruction::CALLDATACOPY: - logTrace(_instruction, arg); if (accessMemory(arg[0], arg[2])) copyZeroExtended( m_state.memory, m_state.calldata, size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) ); + if (arg[2] != 0) + logTrace(_instruction, arg); return 0; case Instruction::CODESIZE: return m_state.code.size(); case Instruction::CODECOPY: - logTrace(_instruction, arg); if (accessMemory(arg[0], arg[2])) copyZeroExtended( m_state.memory, m_state.code, size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) ); + if (arg[2] != 0) + logTrace(_instruction, arg); return 0; case Instruction::GASPRICE: return m_state.gasprice; @@ -239,23 +241,25 @@ u256 EVMInstructionInterpreter::eval( case Instruction::EXTCODEHASH: return u256(keccak256(h256(arg[0] + 1))); case Instruction::EXTCODECOPY: - logTrace(_instruction, arg); if (accessMemory(arg[1], arg[3])) // TODO this way extcodecopy and codecopy do the same thing. copyZeroExtended( m_state.memory, m_state.code, size_t(arg[1]), size_t(arg[2]), size_t(arg[3]) ); + if (arg[3] != 0) + logTrace(_instruction, arg); return 0; case Instruction::RETURNDATASIZE: return m_state.returndata.size(); case Instruction::RETURNDATACOPY: - logTrace(_instruction, arg); if (accessMemory(arg[0], arg[2])) copyZeroExtended( m_state.memory, m_state.returndata, size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) ); + if (arg[2] != 0) + logTrace(_instruction, arg); return 0; case Instruction::BLOCKHASH: if (arg[0] >= m_state.blockNumber || arg[0] + 256 < m_state.blockNumber) @@ -297,38 +301,54 @@ u256 EVMInstructionInterpreter::eval( return 0x99; case Instruction::LOG0: accessMemory(arg[0], arg[1]); - logTrace(_instruction, arg); + if (arg[1] != 0) + logTrace(_instruction, arg); return 0; case Instruction::LOG1: accessMemory(arg[0], arg[1]); - logTrace(_instruction, arg); + if (arg[1] != 0) + logTrace(_instruction, arg); return 0; case Instruction::LOG2: accessMemory(arg[0], arg[1]); - logTrace(_instruction, arg); + if (arg[1] != 0) + logTrace(_instruction, arg); return 0; case Instruction::LOG3: accessMemory(arg[0], arg[1]); - logTrace(_instruction, arg); + if (arg[1] != 0) + logTrace(_instruction, arg); return 0; case Instruction::LOG4: accessMemory(arg[0], arg[1]); - logTrace(_instruction, arg); + if (arg[1] != 0) + logTrace(_instruction, arg); return 0; // --------------- calls --------------- case Instruction::CREATE: accessMemory(arg[1], arg[2]); - logTrace(_instruction, arg); - return (0xcccccc + arg[1]) & u256("0xffffffffffffffffffffffffffffffffffffffff"); + if (arg[2] != 0) + { + logTrace(_instruction, arg); + return (0xcccccc + arg[1]) & u256("0xffffffffffffffffffffffffffffffffffffffff"); + } + return 0xcccccc; case Instruction::CREATE2: accessMemory(arg[1], arg[2]); - logTrace(_instruction, arg); - return (0xdddddd + arg[1]) & u256("0xffffffffffffffffffffffffffffffffffffffff"); + if (arg[2] != 0) + { + logTrace(_instruction, arg); + return (0xdddddd + arg[1]) & u256("0xffffffffffffffffffffffffffffffffffffffff"); + } + return 0xdddddd; case Instruction::CALL: case Instruction::CALLCODE: - accessMemory(arg[3], arg[4]); - accessMemory(arg[5], arg[6]); - logTrace(_instruction, arg); + if (arg[4] != 0) + accessMemory(arg[3], arg[4]); + if (arg[6] != 0) + accessMemory(arg[5], arg[6]); + if (arg[4] != 0 && arg[6] != 0) + logTrace(_instruction, arg); // Randomly fail based on the called address if it isn't a call to self. // Used for fuzzing. return ( @@ -337,10 +357,12 @@ u256 EVMInstructionInterpreter::eval( ) ? 1 : 0; case Instruction::DELEGATECALL: case Instruction::STATICCALL: - accessMemory(arg[2], arg[3]); - accessMemory(arg[4], arg[5]); - logTrace(_instruction, arg); - + if (arg[3] != 0) + accessMemory(arg[2], arg[3]); + if (arg[5] != 0) + accessMemory(arg[4], arg[5]); + if (arg[3] != 0 && arg[5] != 0) + logTrace(_instruction, arg); // Randomly fail based on the called address if it isn't a call to self. // Used for fuzzing. return ( @@ -352,12 +374,14 @@ u256 EVMInstructionInterpreter::eval( m_state.returndata = {}; if (accessMemory(arg[0], arg[1])) m_state.returndata = m_state.readMemory(arg[0], arg[1]); - logTrace(_instruction, arg, m_state.returndata); + if (arg[1] != 0) + logTrace(_instruction, arg, m_state.returndata); BOOST_THROW_EXCEPTION(ExplicitlyTerminatedWithReturn()); } case Instruction::REVERT: accessMemory(arg[0], arg[1]); - logTrace(_instruction, arg); + if (arg[1] != 0) + logTrace(_instruction, arg); m_state.storage.clear(); m_state.trace.clear(); BOOST_THROW_EXCEPTION(ExplicitlyTerminated()); @@ -479,7 +503,10 @@ u256 EVMInstructionInterpreter::evalBuiltin( else if (fun == "datacopy") { // This is identical to codecopy. - if (accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2))) + if ( + _evaluatedArguments.at(2) != 0 && + accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2)) + ) copyZeroExtended( m_state.memory, m_state.code, @@ -560,8 +587,13 @@ void EVMInstructionInterpreter::logTrace( if (!(_writesToMemory && memWriteTracingDisabled())) { string message = _pseudoInstruction + "("; + std::pair inputMemoryPtrModified = isInputMemoryPtrModified(_pseudoInstruction, _arguments); for (size_t i = 0; i < _arguments.size(); ++i) - message += (i > 0 ? ", " : "") + formatNumber(_arguments[i]); + { + bool printZero = inputMemoryPtrModified.first && inputMemoryPtrModified.second == i; + u256 arg = printZero ? 0 : _arguments[i]; + message += (i > 0 ? ", " : "") + formatNumber(arg); + } message += ")"; if (!_data.empty()) message += " [" + util::toHex(_data) + "]"; @@ -573,3 +605,65 @@ void EVMInstructionInterpreter::logTrace( } } } + +std::pair EVMInstructionInterpreter::isInputMemoryPtrModified( + std::string const& _pseudoInstruction, + std::vector const& _arguments +) +{ + if (_pseudoInstruction == "return" || _pseudoInstruction == "revert") + { + if (_arguments[1] == 0) + return {true, 0}; + else + return {false, 0}; + } + else if ( + _pseudoInstruction == "returndatacopy" || _pseudoInstruction == "calldatacopy" + || _pseudoInstruction == "codecopy") + { + if (_arguments[2] == 0) + return {true, 0}; + else + return {false, 0}; + } + else if (_pseudoInstruction == "extcodedatacopy") + { + if (_arguments[3] == 0) + return {true, 1}; + else + return {false, 0}; + } + else if ( + _pseudoInstruction == "log0" || _pseudoInstruction == "log1" || _pseudoInstruction == "log2" + || _pseudoInstruction == "log3" || _pseudoInstruction == "log4") + { + if (_arguments[1] == 0) + return {true, 0}; + else + return {false, 0}; + } + if (_pseudoInstruction == "create" || _pseudoInstruction == "create2") + { + if (_arguments[2] == 0) + return {true, 1}; + else + return {false, 0}; + } + if (_pseudoInstruction == "call" || _pseudoInstruction == "callcode") + { + if (_arguments[4] == 0) + return {true, 3}; + else + return {false, 0}; + } + else if (_pseudoInstruction == "delegatecall" || _pseudoInstruction == "staticcall") + { + if (_arguments[3] == 0) + return {true, 2}; + else + return {false, 0}; + } + else + return {false, 0}; +} diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.h b/test/tools/yulInterpreter/EVMInstructionInterpreter.h index 9f0f473f142b..d37cae542f9d 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.h +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.h @@ -125,6 +125,19 @@ class EVMInstructionInterpreter std::vector const& _arguments = {}, bytes const& _data = {} ); + + /// @returns a pair of boolean and size_t whose first value is true if @param _pseudoInstruction + /// is a Yul instruction that the Yul optimizer's loadResolver step rewrites the input + /// memory pointer value to zero if that instruction's read length (contained within @param + // _arguments) is zero, and whose second value is the positional index of the input memory + // pointer argument. + /// If the Yul instruction is unaffected or affected but read length is non-zero, the first + /// value is false. + std::pair isInputMemoryPtrModified( + std::string const& _pseudoInstruction, + std::vector const& _arguments + ); + /// @returns disable trace flag. bool memWriteTracingDisabled() { From bc00063b2ecdfb37afadc6231f511e4836fcfa0d Mon Sep 17 00:00:00 2001 From: "Rodrigo Q. Saramago" Date: Wed, 1 Feb 2023 10:25:39 +0100 Subject: [PATCH 012/228] Do not print suppressions statistics for cli tests --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 81b757a66ee8..0f5d0da8f84d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1096,7 +1096,7 @@ jobs: ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2 # Suppress CLN memory leak. # See: https://github.com/ethereum/solidity/issues/13891 for details. - LSAN_OPTIONS: suppressions=.circleci/cln-asan.supp + LSAN_OPTIONS: suppressions=.circleci/cln-asan.supp:print_suppressions=0 <<: *steps_cmdline_tests t_ubu_asan_soltest: From 7ddb5e0c6621eaf61ceaef593471753db808e1be Mon Sep 17 00:00:00 2001 From: "Rodrigo Q. Saramago" Date: Thu, 2 Feb 2023 09:48:43 +0100 Subject: [PATCH 013/228] Change relative to absolute path for LSAN_OPTIONS --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0f5d0da8f84d..8a2361492c7a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1096,7 +1096,7 @@ jobs: ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2 # Suppress CLN memory leak. # See: https://github.com/ethereum/solidity/issues/13891 for details. - LSAN_OPTIONS: suppressions=.circleci/cln-asan.supp:print_suppressions=0 + LSAN_OPTIONS: suppressions=/root/project/.circleci/cln-asan.supp:print_suppressions=0 <<: *steps_cmdline_tests t_ubu_asan_soltest: @@ -1109,7 +1109,7 @@ jobs: ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2 # Suppress CLN memory leak. # See: https://github.com/ethereum/solidity/issues/13891 for details. - LSAN_OPTIONS: suppressions=.circleci/cln-asan.supp + LSAN_OPTIONS: suppressions=/root/project/.circleci/cln-asan.supp <<: *steps_soltest t_ubu_asan_clang_soltest: From 5c6e12b2c020d1b8b2981a8a81ae670a990d14b8 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Wed, 1 Feb 2023 08:28:05 +0100 Subject: [PATCH 014/228] Update existing and add new test cases. --- .../libyul/yulInterpreterTests/and_create.yul | 2 + .../yulInterpreterTests/and_create2.yul | 2 + .../external_call_to_self.yul | 1 + .../infinite_recursion_tracelimit.yul | 34 +++++++- .../yulInterpreterTests/pop_byte_shr_call.yul | 1 + .../yulInterpreterTests/zero_length_reads.yul | 40 +++++++++ .../zero_length_reads_and_revert.yul | 40 +++++++++ .../libyul/yulInterpreterTests/zero_range.yul | 1 + .../loadResolver/zero_length_reads.yul | 45 ++++++++++ .../EVMInstructionInterpreter.cpp | 84 +++++++------------ 10 files changed, 197 insertions(+), 53 deletions(-) create mode 100644 test/libyul/yulInterpreterTests/zero_length_reads.yul create mode 100644 test/libyul/yulInterpreterTests/zero_length_reads_and_revert.yul create mode 100644 test/libyul/yulOptimizerTests/loadResolver/zero_length_reads.yul diff --git a/test/libyul/yulInterpreterTests/and_create.yul b/test/libyul/yulInterpreterTests/and_create.yul index cbc68ca623f1..cf32c4658edb 100644 --- a/test/libyul/yulInterpreterTests/and_create.yul +++ b/test/libyul/yulInterpreterTests/and_create.yul @@ -6,6 +6,8 @@ } // ---- // Trace: +// CREATE(0, 0, 0) +// CREATE(0, 0, 0) // Memory dump: // 0: 0000000000000000000000000000000000000000000000000000000000000001 // Storage dump: diff --git a/test/libyul/yulInterpreterTests/and_create2.yul b/test/libyul/yulInterpreterTests/and_create2.yul index 0c0135500dd3..5201b0188a76 100644 --- a/test/libyul/yulInterpreterTests/and_create2.yul +++ b/test/libyul/yulInterpreterTests/and_create2.yul @@ -8,6 +8,8 @@ // EVMVersion: >=constantinople // ---- // Trace: +// CREATE2(0, 0, 0, 0) +// CREATE2(0, 0, 0, 0) // Memory dump: // 0: 0000000000000000000000000000000000000000000000000000000000000001 // Storage dump: diff --git a/test/libyul/yulInterpreterTests/external_call_to_self.yul b/test/libyul/yulInterpreterTests/external_call_to_self.yul index 711159a9fc54..ad2d2bb77f7d 100644 --- a/test/libyul/yulInterpreterTests/external_call_to_self.yul +++ b/test/libyul/yulInterpreterTests/external_call_to_self.yul @@ -14,6 +14,7 @@ // ---- // Trace: // CALL(153, 0x11111111, 0, 64, 32, 256, 32) +// RETURN(0, 0) // Memory dump: // 40: 0000000000000000000000000000000000000000000000000000000000000042 // 100: 0000000000000000000000000000000000000000000000000000000000000042 diff --git a/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul b/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul index 65c28ed5e7cf..45d3cc781c71 100644 --- a/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul +++ b/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul @@ -7,6 +7,38 @@ } // ---- // Trace: -// Interpreter execution step limit reached. +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// Trace size limit reached. // Memory dump: // Storage dump: diff --git a/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul b/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul index 51e0602a3bc8..4e431a6b182e 100644 --- a/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul +++ b/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul @@ -5,5 +5,6 @@ // EVMVersion: >=constantinople // ---- // Trace: +// CALL(0, 0, 0, 0, 0, 0, 0) // Memory dump: // Storage dump: diff --git a/test/libyul/yulInterpreterTests/zero_length_reads.yul b/test/libyul/yulInterpreterTests/zero_length_reads.yul new file mode 100644 index 000000000000..6c1f38d1e9ff --- /dev/null +++ b/test/libyul/yulInterpreterTests/zero_length_reads.yul @@ -0,0 +1,40 @@ +{ + returndatacopy(1, 1, 0) + calldatacopy(1, 1, 0) + extcodecopy(1, 1, 1, 0) + codecopy(1, 1, 0) + log0(1, 0) + log1(1, 0, 1) + log2(1, 0, 1, 1) + log3(1, 0, 1, 1, 1) + log4(1, 0, 1, 1, 1, 1) + pop(create(1, 1, 0)) + pop(create2(1, 1, 0, 1)) + pop(call(1, 1, 1, 1, 0, 1, 0)) + pop(callcode(1, 1, 1, 1, 0, 1, 0)) + pop(delegatecall(1, 1, 1, 0, 1, 0)) + pop(staticcall(1, 1, 1, 0, 1, 0)) + return(1, 0) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Trace: +// RETURNDATACOPY(0, 1, 0) +// CALLDATACOPY(0, 1, 0) +// EXTCODECOPY(1, 0, 1, 0) +// CODECOPY(0, 1, 0) +// LOG0(0, 0) +// LOG1(0, 0, 1) +// LOG2(0, 0, 1, 1) +// LOG3(0, 0, 1, 1, 1) +// LOG4(0, 0, 1, 1, 1, 1) +// CREATE(1, 0, 0) +// CREATE2(1, 0, 0, 1) +// CALL(1, 1, 1, 0, 0, 1, 0) +// CALLCODE(1, 1, 1, 0, 0, 1, 0) +// DELEGATECALL(1, 1, 0, 0, 1, 0) +// STATICCALL(1, 1, 0, 0, 1, 0) +// RETURN(0, 0) +// Memory dump: +// Storage dump: diff --git a/test/libyul/yulInterpreterTests/zero_length_reads_and_revert.yul b/test/libyul/yulInterpreterTests/zero_length_reads_and_revert.yul new file mode 100644 index 000000000000..4be49eeb0977 --- /dev/null +++ b/test/libyul/yulInterpreterTests/zero_length_reads_and_revert.yul @@ -0,0 +1,40 @@ +{ + returndatacopy(1, 1, 0) + calldatacopy(1, 1, 0) + extcodecopy(1, 1, 1, 0) + codecopy(1, 1, 0) + log0(1, 0) + log1(1, 0, 1) + log2(1, 0, 1, 1) + log3(1, 0, 1, 1, 1) + log4(1, 0, 1, 1, 1, 1) + pop(create(1, 1, 0)) + pop(create2(1, 1, 0, 1)) + pop(call(1, 1, 1, 1, 0, 1, 0)) + pop(callcode(1, 1, 1, 1, 0, 1, 0)) + pop(delegatecall(1, 1, 1, 0, 1, 0)) + pop(staticcall(1, 1, 1, 0, 1, 0)) + revert(1, 0) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Trace: +// RETURNDATACOPY(0, 1, 0) +// CALLDATACOPY(0, 1, 0) +// EXTCODECOPY(1, 0, 1, 0) +// CODECOPY(0, 1, 0) +// LOG0(0, 0) +// LOG1(0, 0, 1) +// LOG2(0, 0, 1, 1) +// LOG3(0, 0, 1, 1, 1) +// LOG4(0, 0, 1, 1, 1, 1) +// CREATE(1, 0, 0) +// CREATE2(1, 0, 0, 1) +// CALL(1, 1, 1, 0, 0, 1, 0) +// CALLCODE(1, 1, 1, 0, 0, 1, 0) +// DELEGATECALL(1, 1, 0, 0, 1, 0) +// STATICCALL(1, 1, 0, 0, 1, 0) +// REVERT(0, 0) +// Memory dump: +// Storage dump: diff --git a/test/libyul/yulInterpreterTests/zero_range.yul b/test/libyul/yulInterpreterTests/zero_range.yul index 5e1042dfbcd6..1ad25ed37bca 100644 --- a/test/libyul/yulInterpreterTests/zero_range.yul +++ b/test/libyul/yulInterpreterTests/zero_range.yul @@ -5,5 +5,6 @@ } // ---- // Trace: +// CALLDATACOPY(0, 0, 0) // Memory dump: // Storage dump: diff --git a/test/libyul/yulOptimizerTests/loadResolver/zero_length_reads.yul b/test/libyul/yulOptimizerTests/loadResolver/zero_length_reads.yul new file mode 100644 index 000000000000..418221a93824 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loadResolver/zero_length_reads.yul @@ -0,0 +1,45 @@ +{ + returndatacopy(1, 1, 0) + calldatacopy(1, 1, 0) + extcodecopy(1, 1, 1, 0) + codecopy(1, 1, 0) + log0(1, 0) + log1(1, 0, 1) + log2(1, 0, 1, 1) + log3(1, 0, 1, 1, 1) + log4(1, 0, 1, 1, 1, 1) + pop(create(1, 1, 0)) + pop(create2(1, 1, 0, 1)) + pop(call(1, 1, 1, 1, 0, 1, 0)) + pop(callcode(1, 1, 1, 1, 0, 1, 0)) + pop(delegatecall(1, 1, 1, 0, 1, 0)) + pop(staticcall(1, 1, 1, 0, 1, 0)) + return(1, 0) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// step: loadResolver +// +// { +// { +// let _1 := 0 +// let _2 := 1 +// returndatacopy(0, _2, _1) +// calldatacopy(0, _2, _1) +// extcodecopy(_2, 0, _2, _1) +// codecopy(0, _2, _1) +// log0(0, _1) +// log1(0, _1, _2) +// log2(0, _1, _2, _2) +// log3(0, _1, _2, _2, _2) +// log4(0, _1, _2, _2, _2, _2) +// pop(create(_2, 0, _1)) +// pop(create2(_2, 0, _1, _2)) +// pop(call(_2, _2, _2, 0, _1, _2, _1)) +// pop(callcode(_2, _2, _2, 0, _1, _2, _1)) +// pop(delegatecall(_2, _2, 0, _1, _2, _1)) +// pop(staticcall(_2, _2, 0, _1, _2, _1)) +// return(0, _1) +// } +// } diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index ed73a43813f3..b049ee6c4fdb 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -216,8 +216,7 @@ u256 EVMInstructionInterpreter::eval( m_state.memory, m_state.calldata, size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) ); - if (arg[2] != 0) - logTrace(_instruction, arg); + logTrace(_instruction, arg); return 0; case Instruction::CODESIZE: return m_state.code.size(); @@ -227,8 +226,7 @@ u256 EVMInstructionInterpreter::eval( m_state.memory, m_state.code, size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) ); - if (arg[2] != 0) - logTrace(_instruction, arg); + logTrace(_instruction, arg); return 0; case Instruction::GASPRICE: return m_state.gasprice; @@ -247,8 +245,7 @@ u256 EVMInstructionInterpreter::eval( m_state.memory, m_state.code, size_t(arg[1]), size_t(arg[2]), size_t(arg[3]) ); - if (arg[3] != 0) - logTrace(_instruction, arg); + logTrace(_instruction, arg); return 0; case Instruction::RETURNDATASIZE: return m_state.returndata.size(); @@ -258,8 +255,7 @@ u256 EVMInstructionInterpreter::eval( m_state.memory, m_state.returndata, size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) ); - if (arg[2] != 0) - logTrace(_instruction, arg); + logTrace(_instruction, arg); return 0; case Instruction::BLOCKHASH: if (arg[0] >= m_state.blockNumber || arg[0] + 256 < m_state.blockNumber) @@ -301,54 +297,44 @@ u256 EVMInstructionInterpreter::eval( return 0x99; case Instruction::LOG0: accessMemory(arg[0], arg[1]); - if (arg[1] != 0) - logTrace(_instruction, arg); + logTrace(_instruction, arg); return 0; case Instruction::LOG1: accessMemory(arg[0], arg[1]); - if (arg[1] != 0) - logTrace(_instruction, arg); + logTrace(_instruction, arg); return 0; case Instruction::LOG2: accessMemory(arg[0], arg[1]); - if (arg[1] != 0) - logTrace(_instruction, arg); + logTrace(_instruction, arg); return 0; case Instruction::LOG3: accessMemory(arg[0], arg[1]); - if (arg[1] != 0) - logTrace(_instruction, arg); + logTrace(_instruction, arg); return 0; case Instruction::LOG4: accessMemory(arg[0], arg[1]); - if (arg[1] != 0) - logTrace(_instruction, arg); + logTrace(_instruction, arg); return 0; // --------------- calls --------------- case Instruction::CREATE: accessMemory(arg[1], arg[2]); + logTrace(_instruction, arg); if (arg[2] != 0) - { - logTrace(_instruction, arg); return (0xcccccc + arg[1]) & u256("0xffffffffffffffffffffffffffffffffffffffff"); - } - return 0xcccccc; + else + return 0xcccccc; case Instruction::CREATE2: accessMemory(arg[1], arg[2]); + logTrace(_instruction, arg); if (arg[2] != 0) - { - logTrace(_instruction, arg); return (0xdddddd + arg[1]) & u256("0xffffffffffffffffffffffffffffffffffffffff"); - } - return 0xdddddd; + else + return 0xdddddd; case Instruction::CALL: case Instruction::CALLCODE: - if (arg[4] != 0) - accessMemory(arg[3], arg[4]); - if (arg[6] != 0) - accessMemory(arg[5], arg[6]); - if (arg[4] != 0 && arg[6] != 0) - logTrace(_instruction, arg); + accessMemory(arg[3], arg[4]); + accessMemory(arg[5], arg[6]); + logTrace(_instruction, arg); // Randomly fail based on the called address if it isn't a call to self. // Used for fuzzing. return ( @@ -357,12 +343,9 @@ u256 EVMInstructionInterpreter::eval( ) ? 1 : 0; case Instruction::DELEGATECALL: case Instruction::STATICCALL: - if (arg[3] != 0) - accessMemory(arg[2], arg[3]); - if (arg[5] != 0) - accessMemory(arg[4], arg[5]); - if (arg[3] != 0 && arg[5] != 0) - logTrace(_instruction, arg); + accessMemory(arg[2], arg[3]); + accessMemory(arg[4], arg[5]); + logTrace(_instruction, arg); // Randomly fail based on the called address if it isn't a call to self. // Used for fuzzing. return ( @@ -374,16 +357,13 @@ u256 EVMInstructionInterpreter::eval( m_state.returndata = {}; if (accessMemory(arg[0], arg[1])) m_state.returndata = m_state.readMemory(arg[0], arg[1]); - if (arg[1] != 0) - logTrace(_instruction, arg, m_state.returndata); + logTrace(_instruction, arg, m_state.returndata); BOOST_THROW_EXCEPTION(ExplicitlyTerminatedWithReturn()); } case Instruction::REVERT: accessMemory(arg[0], arg[1]); - if (arg[1] != 0) - logTrace(_instruction, arg); + logTrace(_instruction, arg); m_state.storage.clear(); - m_state.trace.clear(); BOOST_THROW_EXCEPTION(ExplicitlyTerminated()); case Instruction::INVALID: logTrace(_instruction); @@ -611,7 +591,7 @@ std::pair EVMInstructionInterpreter::isInputMemoryPtrModified( std::vector const& _arguments ) { - if (_pseudoInstruction == "return" || _pseudoInstruction == "revert") + if (_pseudoInstruction == "RETURN" || _pseudoInstruction == "REVERT") { if (_arguments[1] == 0) return {true, 0}; @@ -619,15 +599,15 @@ std::pair EVMInstructionInterpreter::isInputMemoryPtrModified( return {false, 0}; } else if ( - _pseudoInstruction == "returndatacopy" || _pseudoInstruction == "calldatacopy" - || _pseudoInstruction == "codecopy") + _pseudoInstruction == "RETURNDATACOPY" || _pseudoInstruction == "CALLDATACOPY" + || _pseudoInstruction == "CODECOPY") { if (_arguments[2] == 0) return {true, 0}; else return {false, 0}; } - else if (_pseudoInstruction == "extcodedatacopy") + else if (_pseudoInstruction == "EXTCODECOPY") { if (_arguments[3] == 0) return {true, 1}; @@ -635,29 +615,29 @@ std::pair EVMInstructionInterpreter::isInputMemoryPtrModified( return {false, 0}; } else if ( - _pseudoInstruction == "log0" || _pseudoInstruction == "log1" || _pseudoInstruction == "log2" - || _pseudoInstruction == "log3" || _pseudoInstruction == "log4") + _pseudoInstruction == "LOG0" || _pseudoInstruction == "LOG1" || _pseudoInstruction == "LOG2" + || _pseudoInstruction == "LOG3" || _pseudoInstruction == "LOG4") { if (_arguments[1] == 0) return {true, 0}; else return {false, 0}; } - if (_pseudoInstruction == "create" || _pseudoInstruction == "create2") + if (_pseudoInstruction == "CREATE" || _pseudoInstruction == "CREATE2") { if (_arguments[2] == 0) return {true, 1}; else return {false, 0}; } - if (_pseudoInstruction == "call" || _pseudoInstruction == "callcode") + if (_pseudoInstruction == "CALL" || _pseudoInstruction == "CALLCODE") { if (_arguments[4] == 0) return {true, 3}; else return {false, 0}; } - else if (_pseudoInstruction == "delegatecall" || _pseudoInstruction == "staticcall") + else if (_pseudoInstruction == "DELEGATECALL" || _pseudoInstruction == "STATICCALL") { if (_arguments[3] == 0) return {true, 2}; From 2b70b08d5f637f2252e84c26d2fbda96385aaf7e Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Sun, 8 Jan 2023 23:14:17 -0300 Subject: [PATCH 015/228] Allow library external functions to be bound with using for --- Changelog.md | 1 + libsolidity/analysis/NameAndTypeResolver.cpp | 15 ++++++--- libsolidity/analysis/NameAndTypeResolver.h | 2 +- libsolidity/analysis/ReferencesResolver.cpp | 33 +++++++++++++++++++ libsolidity/analysis/ReferencesResolver.h | 1 + libsolidity/analysis/TypeChecker.cpp | 20 ++++++++--- .../library_functions_inside_contract.sol | 31 +++++++++++++++++ ...external_library_function_inside_scope.sol | 9 +++++ .../external_function_qualified_with_this.sol | 7 ++++ .../using/free_functions_non_unique_err.sol | 2 +- .../syntaxTests/using/free_overloads.sol | 2 +- .../using/free_overloads_array.sol | 2 +- ...rom_base_contract_qualified_with_super.sol | 9 +++++ ..._name_without_braces_at_file_level_err.sol | 11 +++++++ ...ame_without_braces_inside_contract_err.sol | 10 ++++++ .../interface_function_at_file_level.sol | 7 ++++ .../interface_function_inside_contract.sol | 9 +++++ .../using/library_at_file_level.sol | 17 ++++++++++ .../using/library_functions_at_file_level.sol | 19 +++++++++++ ...ched_at_file_level_used_inside_library.sol | 17 ++++++++++ ...ched_in_single_directive_at_file_level.sol | 17 ++++++++++ ...ed_in_single_directive_inside_contract.sol | 17 ++++++++++ .../library_functions_inside_contract.sol | 19 +++++++++++ .../using/library_inside_contract.sol | 17 ++++++++++ ...library_non_free_external_function_err.sol | 2 +- .../using/module_identifier_not_found.sol | 2 +- .../using/public_state_variable_getter.sol | 11 +++++++ .../syntaxTests/using/undeclared_library.sol | 3 ++ ...hed_in_single_directive_inside_library.sol | 19 +++++++++++ 29 files changed, 316 insertions(+), 15 deletions(-) create mode 100644 test/libsolidity/semanticTests/using/library_functions_inside_contract.sol create mode 100644 test/libsolidity/syntaxTests/scoping/external_library_function_inside_scope.sol create mode 100644 test/libsolidity/syntaxTests/using/external_function_qualified_with_this.sol create mode 100644 test/libsolidity/syntaxTests/using/function_from_base_contract_qualified_with_super.sol create mode 100644 test/libsolidity/syntaxTests/using/function_name_without_braces_at_file_level_err.sol create mode 100644 test/libsolidity/syntaxTests/using/function_name_without_braces_inside_contract_err.sol create mode 100644 test/libsolidity/syntaxTests/using/interface_function_at_file_level.sol create mode 100644 test/libsolidity/syntaxTests/using/interface_function_inside_contract.sol create mode 100644 test/libsolidity/syntaxTests/using/library_at_file_level.sol create mode 100644 test/libsolidity/syntaxTests/using/library_functions_at_file_level.sol create mode 100644 test/libsolidity/syntaxTests/using/library_functions_attached_at_file_level_used_inside_library.sol create mode 100644 test/libsolidity/syntaxTests/using/library_functions_attached_in_single_directive_at_file_level.sol create mode 100644 test/libsolidity/syntaxTests/using/library_functions_attached_in_single_directive_inside_contract.sol create mode 100644 test/libsolidity/syntaxTests/using/library_functions_inside_contract.sol create mode 100644 test/libsolidity/syntaxTests/using/library_inside_contract.sol create mode 100644 test/libsolidity/syntaxTests/using/public_state_variable_getter.sol create mode 100644 test/libsolidity/syntaxTests/using/undeclared_library.sol create mode 100644 test/libsolidity/syntaxTests/using/unqualified_library_functions_attached_in_single_directive_inside_library.sol diff --git a/Changelog.md b/Changelog.md index 8a3bfae6a896..7619fbd40db2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Compiler Features: Bugfixes: + * TypeChecker: Also allow external library functions in ``using for``. ### 0.8.18 (2023-02-01) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 1b58d3c9ab22..641f02280430 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -195,16 +195,23 @@ Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector c return nullptr; } -std::vector NameAndTypeResolver::pathFromCurrentScopeWithAllDeclarations(std::vector const& _path) const +std::vector NameAndTypeResolver::pathFromCurrentScopeWithAllDeclarations( + std::vector const& _path, + bool _includeInvisibles +) const { solAssert(!_path.empty(), ""); vector pathDeclarations; ResolvingSettings settings; settings.recursive = true; - settings.alsoInvisible = false; + settings.alsoInvisible = _includeInvisibles; settings.onlyVisibleAsUnqualifiedNames = true; - vector candidates = m_currentScope->resolveName(_path.front(), std::move(settings)); + vector candidates = m_currentScope->resolveName(_path.front(), settings); + + // inside the loop, use default settings, except for alsoInvisible + settings.recursive = false; + settings.onlyVisibleAsUnqualifiedNames = false; for (size_t i = 1; i < _path.size() && candidates.size() == 1; i++) { @@ -213,7 +220,7 @@ std::vector NameAndTypeResolver::pathFromCurrentScopeWithAll pathDeclarations.push_back(candidates.front()); - candidates = m_scopes.at(candidates.front())->resolveName(_path[i]); + candidates = m_scopes.at(candidates.front())->resolveName(_path[i], settings); } if (candidates.size() == 1) { diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 2bf238a00ee7..6ba591e8ad16 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -96,7 +96,7 @@ class NameAndTypeResolver /// Resolves a path starting from the "current" scope, but also searches parent scopes. /// Should only be called during the initial resolving phase. /// @note Returns an empty vector if any component in the path was non-unique or not found. Otherwise, all declarations along the path are returned. - std::vector pathFromCurrentScopeWithAllDeclarations(std::vector const& _path) const; + std::vector pathFromCurrentScopeWithAllDeclarations(std::vector const& _path, bool _includeInvisibles = false) const; /// Generate and store warnings about declarations with the same name. void warnHomonymDeclarations() const; diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 50b1656ef88b..399c15b56e36 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -173,6 +173,7 @@ void ReferencesResolver::endVisit(ModifierDefinition const&) void ReferencesResolver::endVisit(IdentifierPath const& _path) { + // Note that library/functions names in "using {} for" directive are resolved separately in visit(UsingForDirective) std::vector declarations = m_resolver.pathFromCurrentScopeWithAllDeclarations(_path.path()); if (declarations.empty()) { @@ -184,6 +185,38 @@ void ReferencesResolver::endVisit(IdentifierPath const& _path) _path.annotation().pathDeclarations = std::move(declarations); } +bool ReferencesResolver::visit(UsingForDirective const& _usingFor) +{ + for (ASTPointer const& path: _usingFor.functionsOrLibrary()) + { + // _includeInvisibles is enabled here because external library functions are marked invisible. + // As unintended side-effects other invisible names (eg.: super, this) may be returned as well. + // DeclarationTypeChecker should detect and report such situations. + vector declarations = m_resolver.pathFromCurrentScopeWithAllDeclarations(path->path(), true /* _includeInvisibles */); + if (declarations.empty()) + { + string libraryOrFunctionNameErrorMessage = + _usingFor.usesBraces() ? + "Identifier is not a function name or not unique." : + "Identifier is not a library name."; + m_errorReporter.fatalDeclarationError( + 9589_error, + path->location(), + libraryOrFunctionNameErrorMessage + ); + break; + } + + path->annotation().referencedDeclaration = declarations.back(); + path->annotation().pathDeclarations = std::move(declarations); + } + + if (_usingFor.typeName()) + _usingFor.typeName()->accept(*this); + + return false; +} + bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) { m_yulAnnotation = &_inlineAssembly.annotation(); diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index 530100a24543..512a681b456b 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -84,6 +84,7 @@ class ReferencesResolver: private ASTConstVisitor, private yul::ASTWalker void endVisit(IdentifierPath const& _path) override; bool visit(InlineAssembly const& _inlineAssembly) override; bool visit(Return const& _return) override; + bool visit(UsingForDirective const& _usingFor) override; void operator()(yul::FunctionDefinition const& _function) override; void operator()(yul::Identifier const& _identifier) override; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 4e390931fc6d..074b644b777c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -3826,7 +3826,13 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor) FunctionDefinition const& functionDefinition = dynamic_cast(*path->annotation().referencedDeclaration); - solAssert(functionDefinition.type()); + FunctionType const* functionType = dynamic_cast( + functionDefinition.libraryFunction() ? + functionDefinition.typeViaContractName() : + functionDefinition.type() + ); + + solAssert(functionType); if (functionDefinition.parameters().empty()) m_errorReporter.fatalTypeError( @@ -3864,21 +3870,25 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor) ); } - FunctionType const* functionType = dynamic_cast(*functionDefinition.type()).withBoundFirstArgument(); - solAssert(functionType && functionType->selfType(), ""); + FunctionType const* functionTypeWithBoundFirstArgument = functionType->withBoundFirstArgument(); + solAssert(functionTypeWithBoundFirstArgument && functionTypeWithBoundFirstArgument->selfType(), ""); BoolResult result = normalizedType->isImplicitlyConvertibleTo( - *TypeProvider::withLocationIfReference(DataLocation::Storage, functionType->selfType()) + *TypeProvider::withLocationIfReference(DataLocation::Storage, functionTypeWithBoundFirstArgument->selfType()) ); if (!result) m_errorReporter.typeError( 3100_error, path->location(), + SecondarySourceLocation().append( + "Function defined here:", + functionDefinition.location() + ), fmt::format( "The function \"{}\" cannot be attached to the type \"{}\" because the type cannot " "be implicitly converted to the first argument of the function (\"{}\"){}", joinHumanReadable(path->path(), "."), usingForType->toString(true /* withoutDataLocation */), - functionType->selfType()->humanReadableName(), + functionTypeWithBoundFirstArgument->selfType()->humanReadableName(), result.message().empty() ? "." : ": " + result.message() ) ); diff --git a/test/libsolidity/semanticTests/using/library_functions_inside_contract.sol b/test/libsolidity/semanticTests/using/library_functions_inside_contract.sol new file mode 100644 index 000000000000..170bc7853680 --- /dev/null +++ b/test/libsolidity/semanticTests/using/library_functions_inside_contract.sol @@ -0,0 +1,31 @@ +library L { + function externalFunction(uint a) external pure returns (uint) { return a * 1; } + function publicFunction(uint b) public pure returns (uint) { return b * 2; } + function internalFunction(uint c) internal pure returns (uint) { return c * 3; } +} + +contract C { + using {L.externalFunction} for uint; + using {L.publicFunction} for uint; + using {L.internalFunction} for uint; + + function f() public pure returns (uint) { + uint x = 1; + return x.externalFunction(); + } + + function g() public pure returns (uint) { + uint x = 1; + return x.publicFunction(); + } + + function h() public pure returns (uint) { + uint x = 1; + return x.internalFunction(); + } +} +// ---- +// library: L +// f() -> 1 +// g() -> 2 +// h() -> 3 diff --git a/test/libsolidity/syntaxTests/scoping/external_library_function_inside_scope.sol b/test/libsolidity/syntaxTests/scoping/external_library_function_inside_scope.sol new file mode 100644 index 000000000000..647e5b416e73 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/external_library_function_inside_scope.sol @@ -0,0 +1,9 @@ +library L { + function externalFunction(uint) external pure {} + function f() public pure { + uint x; + externalFunction(x); + } +} +// ---- +// DeclarationError 7576: (120-136): Undeclared identifier. "externalFunction" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/using/external_function_qualified_with_this.sol b/test/libsolidity/syntaxTests/using/external_function_qualified_with_this.sol new file mode 100644 index 000000000000..2f4007605b59 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/external_function_qualified_with_this.sol @@ -0,0 +1,7 @@ +contract C { + using {this.contractFunction} for uint; + + function contractFunction(uint) external view {} +} +// ---- +// DeclarationError 9589: (24-45): Identifier is not a function name or not unique. diff --git a/test/libsolidity/syntaxTests/using/free_functions_non_unique_err.sol b/test/libsolidity/syntaxTests/using/free_functions_non_unique_err.sol index 7536dff07f76..7404b0d9beb8 100644 --- a/test/libsolidity/syntaxTests/using/free_functions_non_unique_err.sol +++ b/test/libsolidity/syntaxTests/using/free_functions_non_unique_err.sol @@ -9,4 +9,4 @@ contract C { using {id} for uint256; } // ---- -// DeclarationError 7920: (145-147): Identifier not found or not unique. +// DeclarationError 9589: (145-147): Identifier is not a function name or not unique. diff --git a/test/libsolidity/syntaxTests/using/free_overloads.sol b/test/libsolidity/syntaxTests/using/free_overloads.sol index 28a8e69ed1ce..e05580d5fb1e 100644 --- a/test/libsolidity/syntaxTests/using/free_overloads.sol +++ b/test/libsolidity/syntaxTests/using/free_overloads.sol @@ -7,4 +7,4 @@ function f(int8 storage x) pure returns (int) { using {f} for uint8; using {f} for int; // ---- -// DeclarationError 7920: (132-133): Identifier not found or not unique. +// DeclarationError 9589: (132-133): Identifier is not a function name or not unique. diff --git a/test/libsolidity/syntaxTests/using/free_overloads_array.sol b/test/libsolidity/syntaxTests/using/free_overloads_array.sol index 91b807397112..cab3eccf2467 100644 --- a/test/libsolidity/syntaxTests/using/free_overloads_array.sol +++ b/test/libsolidity/syntaxTests/using/free_overloads_array.sol @@ -6,4 +6,4 @@ function f(uint x, uint y) pure returns (int) { } using {f} for uint; // ---- -// DeclarationError 7920: (138-139): Identifier not found or not unique. +// DeclarationError 9589: (138-139): Identifier is not a function name or not unique. diff --git a/test/libsolidity/syntaxTests/using/function_from_base_contract_qualified_with_super.sol b/test/libsolidity/syntaxTests/using/function_from_base_contract_qualified_with_super.sol new file mode 100644 index 000000000000..a162912cad20 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/function_from_base_contract_qualified_with_super.sol @@ -0,0 +1,9 @@ +contract C { + function baseFunction(uint) public pure {} +} + +contract D is C { + using {super.baseFunction} for uint; +} +// ---- +// DeclarationError 9589: (92-110): Identifier is not a function name or not unique. diff --git a/test/libsolidity/syntaxTests/using/function_name_without_braces_at_file_level_err.sol b/test/libsolidity/syntaxTests/using/function_name_without_braces_at_file_level_err.sol new file mode 100644 index 000000000000..85d591fd47b4 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/function_name_without_braces_at_file_level_err.sol @@ -0,0 +1,11 @@ +function f(uint x) pure { } + +using f for uint; + +contract C { + function g(uint x) public pure { + x.f(); + } +} +// ---- +// TypeError 4357: (35-36): Library name expected. If you want to attach a function, use '{...}'. diff --git a/test/libsolidity/syntaxTests/using/function_name_without_braces_inside_contract_err.sol b/test/libsolidity/syntaxTests/using/function_name_without_braces_inside_contract_err.sol new file mode 100644 index 000000000000..003137381785 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/function_name_without_braces_inside_contract_err.sol @@ -0,0 +1,10 @@ +function f(uint x) pure { } + +contract C { + using f for uint; + function g(uint x) public pure { + x.f(); + } +} +// ---- +// TypeError 4357: (52-53): Library name expected. If you want to attach a function, use '{...}'. diff --git a/test/libsolidity/syntaxTests/using/interface_function_at_file_level.sol b/test/libsolidity/syntaxTests/using/interface_function_at_file_level.sol new file mode 100644 index 000000000000..9a9ee26f6111 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/interface_function_at_file_level.sol @@ -0,0 +1,7 @@ +interface I { + function g() external pure; +} + +using {I.g} for uint; +// ---- +// TypeError 4167: (56-59): Only file-level functions and library functions can be attached to a type in a "using" statement diff --git a/test/libsolidity/syntaxTests/using/interface_function_inside_contract.sol b/test/libsolidity/syntaxTests/using/interface_function_inside_contract.sol new file mode 100644 index 000000000000..f01788ff5170 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/interface_function_inside_contract.sol @@ -0,0 +1,9 @@ +interface I { + function g() external pure; +} + +contract C { + using {I.g} for uint; +} +// ---- +// TypeError 4167: (73-76): Only file-level functions and library functions can be attached to a type in a "using" statement diff --git a/test/libsolidity/syntaxTests/using/library_at_file_level.sol b/test/libsolidity/syntaxTests/using/library_at_file_level.sol new file mode 100644 index 000000000000..9cb1965a0864 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/library_at_file_level.sol @@ -0,0 +1,17 @@ +library L { + function externalFunction(uint) external pure {} + function publicFunction(uint) public pure {} + function internalFunction(uint) internal pure {} +} + +using L for uint; + +contract C { + function f() public pure { + uint x; + x.externalFunction(); + x.publicFunction(); + x.internalFunction(); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/using/library_functions_at_file_level.sol b/test/libsolidity/syntaxTests/using/library_functions_at_file_level.sol new file mode 100644 index 000000000000..2876f591a09c --- /dev/null +++ b/test/libsolidity/syntaxTests/using/library_functions_at_file_level.sol @@ -0,0 +1,19 @@ +library L { + function externalFunction(uint) external pure {} + function publicFunction(uint) public pure {} + function internalFunction(uint) internal pure {} +} + +using {L.externalFunction} for uint; +using {L.publicFunction} for uint; +using {L.internalFunction} for uint; + +contract C { + function f() public pure { + uint x; + x.externalFunction(); + x.publicFunction(); + x.internalFunction(); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/using/library_functions_attached_at_file_level_used_inside_library.sol b/test/libsolidity/syntaxTests/using/library_functions_attached_at_file_level_used_inside_library.sol new file mode 100644 index 000000000000..21d30f557294 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/library_functions_attached_at_file_level_used_inside_library.sol @@ -0,0 +1,17 @@ +using {L.externalFunction, L.publicFunction, L.internalFunction} for uint; + +library L { + function externalFunction(uint) external pure {} + function publicFunction(uint) public pure {} + function internalFunction(uint) internal pure {} + + function f() public pure { + uint x; + x.externalFunction(); + x.publicFunction(); + x.internalFunction(); + } +} +// ---- +// TypeError 6700: (299-319): Libraries cannot call their own functions externally. +// TypeError 6700: (329-347): Libraries cannot call their own functions externally. diff --git a/test/libsolidity/syntaxTests/using/library_functions_attached_in_single_directive_at_file_level.sol b/test/libsolidity/syntaxTests/using/library_functions_attached_in_single_directive_at_file_level.sol new file mode 100644 index 000000000000..bb191faa6449 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/library_functions_attached_in_single_directive_at_file_level.sol @@ -0,0 +1,17 @@ +library L { + function externalFunction(uint) external pure {} + function publicFunction(uint) public pure {} + function internalFunction(uint) internal pure {} +} + +using {L.externalFunction, L.publicFunction, L.internalFunction} for uint; + +contract C { + function f() public pure { + uint x; + x.externalFunction(); + x.publicFunction(); + x.internalFunction(); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/using/library_functions_attached_in_single_directive_inside_contract.sol b/test/libsolidity/syntaxTests/using/library_functions_attached_in_single_directive_inside_contract.sol new file mode 100644 index 000000000000..300a3480d38c --- /dev/null +++ b/test/libsolidity/syntaxTests/using/library_functions_attached_in_single_directive_inside_contract.sol @@ -0,0 +1,17 @@ +library L { + function externalFunction(uint) external pure {} + function publicFunction(uint) public pure {} + function internalFunction(uint) internal pure {} +} + +contract C { + using {L.externalFunction, L.publicFunction, L.internalFunction} for uint; + + function f() public pure { + uint x; + x.externalFunction(); + x.publicFunction(); + x.internalFunction(); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/using/library_functions_inside_contract.sol b/test/libsolidity/syntaxTests/using/library_functions_inside_contract.sol new file mode 100644 index 000000000000..46d4c571d6d4 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/library_functions_inside_contract.sol @@ -0,0 +1,19 @@ +library L { + function externalFunction(uint) external pure {} + function publicFunction(uint) public pure {} + function internalFunction(uint) internal pure {} +} + +contract C { + using {L.externalFunction} for uint; + using {L.publicFunction} for uint; + using {L.internalFunction} for uint; + + function f() public pure { + uint x; + x.externalFunction(); + x.publicFunction(); + x.internalFunction(); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/using/library_inside_contract.sol b/test/libsolidity/syntaxTests/using/library_inside_contract.sol new file mode 100644 index 000000000000..cd7f8fd19386 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/library_inside_contract.sol @@ -0,0 +1,17 @@ +library L { + function externalFunction(uint) external pure {} + function publicFunction(uint) public pure {} + function internalFunction(uint) internal pure {} +} + +contract C { + using L for uint; + + function f() public pure { + uint x; + x.externalFunction(); + x.publicFunction(); + x.internalFunction(); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/using/library_non_free_external_function_err.sol b/test/libsolidity/syntaxTests/using/library_non_free_external_function_err.sol index 76d53cf90cf4..0a51043db2ad 100644 --- a/test/libsolidity/syntaxTests/using/library_non_free_external_function_err.sol +++ b/test/libsolidity/syntaxTests/using/library_non_free_external_function_err.sol @@ -11,4 +11,4 @@ contract C { } } // ---- -// DeclarationError 7920: (115-123): Identifier not found or not unique. +// TypeError 4357: (115-123): Library name expected. If you want to attach a function, use '{...}'. diff --git a/test/libsolidity/syntaxTests/using/module_identifier_not_found.sol b/test/libsolidity/syntaxTests/using/module_identifier_not_found.sol index ef1688b22915..2526d216f252 100644 --- a/test/libsolidity/syntaxTests/using/module_identifier_not_found.sol +++ b/test/libsolidity/syntaxTests/using/module_identifier_not_found.sol @@ -10,4 +10,4 @@ contract C { using { id } for uint; } // ---- -// DeclarationError 7920: (B:43-45): Identifier not found or not unique. +// DeclarationError 9589: (B:43-45): Identifier is not a function name or not unique. diff --git a/test/libsolidity/syntaxTests/using/public_state_variable_getter.sol b/test/libsolidity/syntaxTests/using/public_state_variable_getter.sol new file mode 100644 index 000000000000..9223a92a5daa --- /dev/null +++ b/test/libsolidity/syntaxTests/using/public_state_variable_getter.sol @@ -0,0 +1,11 @@ +contract A { + uint public data; +} + +contract C { + A a = new A(); + + using {a.data} for uint; +} +// ---- +// DeclarationError 9589: (82-88): Identifier is not a function name or not unique. diff --git a/test/libsolidity/syntaxTests/using/undeclared_library.sol b/test/libsolidity/syntaxTests/using/undeclared_library.sol new file mode 100644 index 000000000000..c2174cc6c124 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/undeclared_library.sol @@ -0,0 +1,3 @@ +using L for uint; +// ---- +// DeclarationError 9589: (6-7): Identifier is not a library name. diff --git a/test/libsolidity/syntaxTests/using/unqualified_library_functions_attached_in_single_directive_inside_library.sol b/test/libsolidity/syntaxTests/using/unqualified_library_functions_attached_in_single_directive_inside_library.sol new file mode 100644 index 000000000000..7403dcf28247 --- /dev/null +++ b/test/libsolidity/syntaxTests/using/unqualified_library_functions_attached_in_single_directive_inside_library.sol @@ -0,0 +1,19 @@ +library L { + function externalFunction(uint) external pure {} + function publicFunction(uint) public pure {} + function internalFunction(uint) internal pure {} + function privateFunction(uint) private pure {} + + using {externalFunction, publicFunction, internalFunction, privateFunction} for uint; + + function f() public pure { + uint x; + x.externalFunction(); + x.publicFunction(); + x.internalFunction(); + x.privateFunction(); + } +} +// ---- +// TypeError 6700: (365-385): Libraries cannot call their own functions externally. +// TypeError 6700: (395-413): Libraries cannot call their own functions externally. From 6f285ad19758b0f8bfb20183c1b8d6b48fdd4a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Sun, 4 Dec 2022 23:20:48 +0100 Subject: [PATCH 016/228] Update debian/compat to version 13 Compat version 13 is currently the recommended one. An important change introduced in 10 was change of default to target parallel builds https://github.com/Debian/debhelper/blob/5d1bb29841043d8e47ebbdd043e6cd086cad508e/debhelper.pod#compatibility-levels --- scripts/deps-ppa/static_z3.sh | 6 +++--- scripts/release_ppa.sh | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/deps-ppa/static_z3.sh b/scripts/deps-ppa/static_z3.sh index fa237ba08f7a..35fff526f904 100755 --- a/scripts/deps-ppa/static_z3.sh +++ b/scripts/deps-ppa/static_z3.sh @@ -73,16 +73,16 @@ cp "/tmp/${packagename}_${debversion}.orig.tar.gz" ../ # Create debian package information mkdir debian -echo 9 > debian/compat +echo 13 > debian/compat # TODO: the Z3 packages have different build dependencies cat < debian/control Source: z3-static Section: science Priority: extra Maintainer: Daniel Kirchner -Build-Depends: debhelper (>= 9.0.0), +Build-Depends: debhelper (>= 13.0.0), cmake, - g++ (>= 5.0), + g++ (>= 9.0), git, libgmp-dev, dh-python, diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index 2acd62cd3680..cf3e4b3356d5 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -156,15 +156,15 @@ cp "/tmp/${packagename}_${debversion}.orig.tar.gz" ../ # Create debian package information mkdir debian -echo 9 > debian/compat +echo 13 > debian/compat cat < debian/control Source: solc Section: science Priority: extra Maintainer: Christian (Buildserver key) -Build-Depends: ${SMTDEPENDENCY}debhelper (>= 9.0.0), +Build-Depends: ${SMTDEPENDENCY}debhelper (>= 13.0.0), cmake, - g++ (>= 5.0), + g++ (>= 9.0), git, libgmp-dev, libboost-all-dev, From 43431eb427c6086701353aeda94b8ae9b401b1e9 Mon Sep 17 00:00:00 2001 From: Jacob Heider Date: Fri, 3 Feb 2023 21:37:50 -0500 Subject: [PATCH 017/228] Fixes compilation errors with some clangs resolves https://github.com/ethereum/solidity/issues/13854 --- libsolidity/lsp/DocumentHoverHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/lsp/DocumentHoverHandler.cpp b/libsolidity/lsp/DocumentHoverHandler.cpp index 628b62fa1ffd..77000222892c 100644 --- a/libsolidity/lsp/DocumentHoverHandler.cpp +++ b/libsolidity/lsp/DocumentHoverHandler.cpp @@ -63,7 +63,7 @@ void DocumentHoverHandler::operator()(MessageID _id, Json::Value const& _args) auto const [sourceUnitName, lineColumn] = HandlerBase(*this).extractSourceUnitNameAndLineColumn(_args); auto const [sourceNode, sourceOffset] = m_server.astNodeAndOffsetAtSourceLocation(sourceUnitName, lineColumn); - MarkdownBuilder markdown{}; + MarkdownBuilder markdown; auto rangeToHighlight = toRange(sourceNode->location()); // Try getting the type definition of the underlying AST node, if available. From 8f1668ffb8ca21fa8bc95674bc366a6069a586d8 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Wed, 1 Feb 2023 20:00:20 +0100 Subject: [PATCH 018/228] libsolutil: Add missing include This helps suppressing the following message during compilation: ``` /builddir/build/BUILD/solidity-0.8.18/libsolutil/Common.h:55:27: error: 'uint8_t' was not declared in this scope 55 | using bytes = std::vector; | ^~~~~~~ /builddir/build/BUILD/solidity-0.8.18/libsolutil/Common.h:49:1: note: 'uint8_t' is defined in header ''; did you forget to '#include '? 48 | #include +++ |+#include 49 | #include ``` Signed-off-by: Peter Lemenkov --- libsolutil/Common.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libsolutil/Common.h b/libsolutil/Common.h index eea9dacd5451..c908c13ae2bc 100644 --- a/libsolutil/Common.h +++ b/libsolutil/Common.h @@ -42,11 +42,12 @@ #include +#include +#include #include +#include #include #include -#include -#include namespace solidity { From 6a6bf303b5ef7a000e56d4b0e87d882e7287e1ea Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Wed, 1 Feb 2023 20:00:20 +0100 Subject: [PATCH 019/228] libsolutil: Add missing include This one is more obscure. It helps suppressing the following error while compiling: ``` /builddir/build/BUILD/solidity-0.8.18/liblangutil/EVMVersion.h:33:6: error: elaborated-type-specifier for a scoped enum must not use the 'class' keyword [-Werror] 33 | enum class Instruction: uint8_t; | ~~~~ ^~~~~ | ----- /builddir/build/BUILD/solidity-0.8.18/liblangutil/EVMVersion.h:33:23: error: found ':' in nested-name-specifier, expected '::' 33 | enum class Instruction: uint8_t; | ^ | :: /builddir/build/BUILD/solidity-0.8.18/liblangutil/EVMVersion.h:33:12: error: 'Instruction' has not been declared 33 | enum class Instruction: uint8_t; | ^~~~~~~~~~~ /builddir/build/BUILD/solidity-0.8.18/liblangutil/EVMVersion.h:101:24: error: 'solidity::evmasm::Instruction' has not been declared 101 | bool hasOpcode(evmasm::Instruction _opcode) const; | ^~~~~~ /builddir/build/BUILD/solidity-0.8.18/liblangutil/EVMVersion.cpp:29:6: error: no declaration matches 'bool solidity::langutil::EVMVersion::hasOpcode(solidity::evmasm::Instruction) const' 29 | bool EVMVersion::hasOpcode(Instruction _opcode) const | ^~~~~~~~~~ /builddir/build/BUILD/solidity-0.8.18/liblangutil/EVMVersion.h:101:14: note: candidate is: 'bool solidity::langutil::EVMVersion::hasOpcode(int) const' 101 | bool hasOpcode(evmasm::Instruction _opcode) const; | ^~~~~~~~~ /builddir/build/BUILD/solidity-0.8.18/liblangutil/EVMVersion.h:43:7: note: 'class solidity::langutil::EVMVersion' defined here 43 | class EVMVersion: | ^~~~~~~~~~ cc1plus: all warnings being treated as errors ``` Signed-off-by: Peter Lemenkov --- liblangutil/EVMVersion.h | 1 + 1 file changed, 1 insertion(+) diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h index aa23ea6eef92..22f85c8af6e4 100644 --- a/liblangutil/EVMVersion.h +++ b/liblangutil/EVMVersion.h @@ -21,6 +21,7 @@ #pragma once +#include #include #include From b3f35f703aa5d2f55cff36b9a6f16c0ac7d44849 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Sat, 4 Feb 2023 16:16:54 +0100 Subject: [PATCH 020/228] libsolidity: Redundant std::move This patch suppresses warnings like this one: ``` /builddir/build/BUILD/solidity-0.8.18/libsolidity/ast/AST.h: In constructor 'solidity::frontend::FunctionDefinition::FunctionDefinition(int64_t, const solidity::frontend::ASTNode::SourceLocation&, solidity::frontend::ASTPointer >&, const solidity::frontend::ASTNode::SourceLocation&, solidity::frontend::Visibility, solidity::frontend::StateMutability, bool, solidity::langutil::Token, bool, solidity::frontend::ASTPointer&, solidity::frontend::ASTPointer&, solidity::frontend::ASTPointer&, std::vector >, solidity::frontend::ASTPointer&, solidity::frontend::ASTPointer&)': /builddir/build/BUILD/solidity-0.8.18/libsolidity/ast/AST.h:926:69: error: redundant move in initialization [-Werror=redundant-move] 926 | CallableDeclaration(_id, _location, _name, std::move(_nameLocation), _visibility, _parameters, _isVirtual, _overrides, _returnParameters), | ~~~~~~~~~^~~~~~~~~~~~~~~ /builddir/build/BUILD/solidity-0.8.18/libsolidity/ast/AST.h:926:69: note: remove 'std::move' call ``` Signed-off-by: Peter Lemenkov --- libsolidity/ast/AST.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 06575beece0a..f0f43945bfbc 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -923,7 +923,7 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen ASTPointer const& _returnParameters, ASTPointer const& _body ): - CallableDeclaration(_id, _location, _name, std::move(_nameLocation), _visibility, _parameters, _isVirtual, _overrides, _returnParameters), + CallableDeclaration(_id, _location, _name, _nameLocation, _visibility, _parameters, _isVirtual, _overrides, _returnParameters), StructurallyDocumented(_documentation), ImplementationOptional(_body != nullptr), m_stateMutability(_stateMutability), From 821da895ea81167ded6d7f879b8793f9a3ba251e Mon Sep 17 00:00:00 2001 From: Evan Saulpaugh Date: Sun, 5 Feb 2023 16:13:23 -0600 Subject: [PATCH 021/228] specify "receive" as having no "name" --- docs/abi-spec.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 35123a30a220..44988c8dc178 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -563,7 +563,7 @@ A function description is a JSON object with the fields: blockchain state `), ``view`` (:ref:`specified to not modify the blockchain state `), ``nonpayable`` (function does not accept Ether - the default) and ``payable`` (function accepts Ether). -Constructor and fallback function never have ``name`` or ``outputs``. Fallback function doesn't have ``inputs`` either. +Constructor, receive, and fallback never have ``name`` or ``outputs``. Receive and fallback don't have ``inputs`` either. .. note:: Sending non-zero Ether to non-payable function will revert the transaction. From 8d91ccf0284b4baa00f4438ad099f9f44a5ff8f1 Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Tue, 12 Oct 2021 11:12:18 +0200 Subject: [PATCH 022/228] [SMTChecker] Add a new trusted mode which assumes that code that is available at compile time is trusted. --- Changelog.md | 1 + docs/smtchecker.rst | 182 +++++++ docs/using-the-compiler.rst | 4 + libsolidity/formal/BMC.cpp | 6 +- libsolidity/formal/CHC.cpp | 486 ++++++++++++++---- libsolidity/formal/CHC.h | 23 + libsolidity/formal/ModelCheckerSettings.cpp | 9 + libsolidity/formal/ModelCheckerSettings.h | 17 + libsolidity/formal/Predicate.cpp | 10 + libsolidity/formal/Predicate.h | 4 + libsolidity/formal/PredicateInstance.cpp | 11 +- libsolidity/formal/PredicateInstance.h | 12 +- libsolidity/formal/SMTEncoder.cpp | 47 +- libsolidity/formal/SMTEncoder.h | 15 +- libsolidity/formal/SymbolicState.cpp | 200 ++++++- libsolidity/formal/SymbolicState.h | 57 +- libsolidity/formal/SymbolicTypes.cpp | 22 + libsolidity/formal/SymbolicTypes.h | 4 + libsolidity/interface/StandardCompiler.cpp | 12 +- solc/CommandLineParser.cpp | 17 + .../err | 26 + .../model_checker_contracts_only_one/err | 2 +- .../model_checker_ext_calls_empty_arg/args | 1 + .../model_checker_ext_calls_empty_arg/err | 1 + .../model_checker_ext_calls_empty_arg/exit | 1 + .../input.sol | 15 + .../model_checker_ext_calls_trusted_chc/args | 1 + .../model_checker_ext_calls_trusted_chc/err | 5 + .../input.sol | 15 + .../args | 1 + .../model_checker_ext_calls_untrusted_chc/err | 14 + .../input.sol | 13 + .../model_checker_ext_calls_wrong_arg/args | 1 + .../model_checker_ext_calls_wrong_arg/err | 1 + .../model_checker_ext_calls_wrong_arg/exit | 1 + .../input.sol | 15 + .../output.json | 68 +++ .../output.json | 4 +- .../output.json | 4 +- .../input.json | 30 ++ .../output.json | 12 + .../input.json | 30 ++ .../output.json | 32 ++ .../input.json | 28 + .../output.json | 50 ++ .../input.json | 30 ++ .../output.json | 12 + .../input.json | 30 ++ .../output.json | 12 + .../output.json | 62 +-- test/libsolidity/SMTCheckerTest.cpp | 15 +- .../length_1d_struct_array_2d_1.sol | 2 +- .../length_same_after_assignment_3_fail.sol | 2 +- .../array_members/push_as_lhs_1d.sol | 1 + .../array_members/push_as_lhs_2d.sol | 1 + .../complex/slither/external_function.sol | 4 +- .../deployment/deploy_bmc_trusted.sol | 18 + .../deployment/deploy_bmc_untrusted.sol | 17 + .../deployment/deploy_trusted.sol | 16 + .../deployment/deploy_trusted_addresses.sol | 19 + .../deployment/deploy_trusted_flow.sol | 32 ++ ...eploy_trusted_keep_storage_constraints.sol | 17 + .../deployment/deploy_trusted_state_flow.sol | 24 + .../deploy_trusted_state_flow_2.sol | 20 + .../deploy_trusted_state_flow_3.sol | 21 + .../deploy_trusted_state_flow_4.sol | 20 + .../deployment/deploy_untrusted.sol | 17 + .../deployment/deploy_untrusted_addresses.sol | 22 + ...oy_untrusted_erase_storage_constraints.sol | 17 + .../deployment_trusted_with_value_1.sol | 20 + .../call_abstract_constructor_trusted_1.sol | 28 + .../call_abstract_constructor_trusted_2.sol | 25 + .../call_abstract_trusted_1.sol | 28 + .../call_abstract_trusted_2.sol | 25 + .../call_abstract_trusted_3.sol | 29 ++ .../external_calls/call_reentrancy_view.sol | 2 +- ...ternal_call_from_constructor_1_trusted.sol | 21 + ...ternal_call_from_constructor_2_trusted.sol | 17 + ...ternal_call_from_constructor_3_trusted.sol | 25 + .../external_call_indirect_1.sol | 41 ++ .../external_call_indirect_2.sol | 52 ++ .../external_call_indirect_3.sol | 45 ++ .../external_call_indirect_4.sol | 48 ++ .../external_call_indirect_5.sol | 50 ++ .../external_call_semantic_this_1.sol | 17 + .../external_call_semantic_this_2.sol | 16 + .../external_call_semantic_this_3.sol | 17 + ..._address_inside_array_struct_trusted_1.sol | 23 + ..._address_inside_array_struct_trusted_2.sol | 24 + ...ate_var_address_inside_array_trusted_1.sol | 21 + ...ate_var_address_inside_array_trusted_2.sol | 20 + ...te_var_address_inside_struct_trusted_1.sol | 24 + ...te_var_address_inside_struct_trusted_2.sol | 23 + ...te_var_address_inside_struct_trusted_3.sol | 26 + ...te_var_address_inside_struct_trusted_4.sol | 27 + ...contract_inside_array_struct_trusted_1.sol | 23 + ...contract_inside_array_struct_trusted_2.sol | 24 + ...te_var_contract_inside_array_trusted_1.sol | 21 + ...te_var_contract_inside_array_trusted_2.sol | 21 + ...e_var_contract_inside_struct_trusted_1.sol | 24 + ...e_var_contract_inside_struct_trusted_2.sol | 24 + ...e_var_contract_inside_struct_trusted_3.sol | 27 + ...e_var_contract_inside_struct_trusted_4.sol | 27 + .../external_call_this_with_value_1.sol | 2 +- .../external_call_this_with_value_2.sol | 6 +- .../external_hash_known_code_pure_trusted.sol | 33 ++ ..._known_code_state_reentrancy_2_trusted.sol | 47 ++ ...code_state_reentrancy_indirect_trusted.sol | 56 ++ ...sh_known_code_state_reentrancy_trusted.sol | 38 ++ ...n_code_state_reentrancy_unsafe_trusted.sol | 44 ++ ...external_hash_known_code_state_trusted.sol | 36 ++ ...l_hash_known_code_state_unsafe_trusted.sol | 40 ++ .../external_calls/external_reentrancy_2.sol | 2 +- .../external_calls/external_safe.sol | 2 + .../token_trusted_transfer_correct.sol | 64 +++ .../token_trusted_transfer_wrong.sol | 68 +++ .../file_level/new_operator.sol | 2 +- .../functions/functions_external_2.sol | 2 +- .../functions/getters/external_getter_1.sol | 22 + .../functions/getters/external_getter_2.sol | 33 ++ .../getters/external_getter_this_1.sol | 18 + .../getters/external_getter_this_2.sol | 19 + ...virtual_function_called_by_constructor.sol | 2 +- .../smtCheckerTests/imports/ExtCall.sol | 2 +- .../imports/import_as_module_2.sol | 1 + .../operators/compound_mul_mapping.sol | 3 +- .../operators/conditional_assignment_6.sol | 2 +- .../special/tx_vars_reentrancy_1.sol | 1 + .../smtCheckerTests/try_catch/try_new.sol | 4 +- .../typecast/bytes_to_fixed_bytes_1.sol | 3 + .../typecast/string_to_bytes_push_1.sol | 2 +- .../smtCheckerTests/types/bool_simple_2.sol | 1 + .../user_type_as_struct_member_1.sol | 1 - test/solc/CommandLineParser.cpp | 2 + test/tools/fuzzer_common.cpp | 1 + 135 files changed, 3153 insertions(+), 232 deletions(-) create mode 100644 test/cmdlineTests/model_checker_ext_calls_empty_arg/args create mode 100644 test/cmdlineTests/model_checker_ext_calls_empty_arg/err create mode 100644 test/cmdlineTests/model_checker_ext_calls_empty_arg/exit create mode 100644 test/cmdlineTests/model_checker_ext_calls_empty_arg/input.sol create mode 100644 test/cmdlineTests/model_checker_ext_calls_trusted_chc/args create mode 100644 test/cmdlineTests/model_checker_ext_calls_trusted_chc/err create mode 100644 test/cmdlineTests/model_checker_ext_calls_trusted_chc/input.sol create mode 100644 test/cmdlineTests/model_checker_ext_calls_untrusted_chc/args create mode 100644 test/cmdlineTests/model_checker_ext_calls_untrusted_chc/err create mode 100644 test/cmdlineTests/model_checker_ext_calls_untrusted_chc/input.sol create mode 100644 test/cmdlineTests/model_checker_ext_calls_wrong_arg/args create mode 100644 test/cmdlineTests/model_checker_ext_calls_wrong_arg/err create mode 100644 test/cmdlineTests/model_checker_ext_calls_wrong_arg/exit create mode 100644 test/cmdlineTests/model_checker_ext_calls_wrong_arg/input.sol create mode 100644 test/cmdlineTests/standard_model_checker_ext_calls_empty_arg/input.json create mode 100644 test/cmdlineTests/standard_model_checker_ext_calls_empty_arg/output.json create mode 100644 test/cmdlineTests/standard_model_checker_ext_calls_trusted_chc/input.json create mode 100644 test/cmdlineTests/standard_model_checker_ext_calls_trusted_chc/output.json create mode 100644 test/cmdlineTests/standard_model_checker_ext_calls_untrusted_chc/input.json create mode 100644 test/cmdlineTests/standard_model_checker_ext_calls_untrusted_chc/output.json create mode 100644 test/cmdlineTests/standard_model_checker_ext_calls_wrong_arg_1/input.json create mode 100644 test/cmdlineTests/standard_model_checker_ext_calls_wrong_arg_1/output.json create mode 100644 test/cmdlineTests/standard_model_checker_ext_calls_wrong_arg_2/input.json create mode 100644 test/cmdlineTests/standard_model_checker_ext_calls_wrong_arg_2/output.json create mode 100644 test/libsolidity/smtCheckerTests/deployment/deploy_bmc_trusted.sol create mode 100644 test/libsolidity/smtCheckerTests/deployment/deploy_bmc_untrusted.sol create mode 100644 test/libsolidity/smtCheckerTests/deployment/deploy_trusted.sol create mode 100644 test/libsolidity/smtCheckerTests/deployment/deploy_trusted_addresses.sol create mode 100644 test/libsolidity/smtCheckerTests/deployment/deploy_trusted_flow.sol create mode 100644 test/libsolidity/smtCheckerTests/deployment/deploy_trusted_keep_storage_constraints.sol create mode 100644 test/libsolidity/smtCheckerTests/deployment/deploy_trusted_state_flow.sol create mode 100644 test/libsolidity/smtCheckerTests/deployment/deploy_trusted_state_flow_2.sol create mode 100644 test/libsolidity/smtCheckerTests/deployment/deploy_trusted_state_flow_3.sol create mode 100644 test/libsolidity/smtCheckerTests/deployment/deploy_trusted_state_flow_4.sol create mode 100644 test/libsolidity/smtCheckerTests/deployment/deploy_untrusted.sol create mode 100644 test/libsolidity/smtCheckerTests/deployment/deploy_untrusted_addresses.sol create mode 100644 test/libsolidity/smtCheckerTests/deployment/deploy_untrusted_erase_storage_constraints.sol create mode 100644 test/libsolidity/smtCheckerTests/deployment/deployment_trusted_with_value_1.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/call_abstract_constructor_trusted_1.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/call_abstract_constructor_trusted_2.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/call_abstract_trusted_1.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/call_abstract_trusted_2.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/call_abstract_trusted_3.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_1_trusted.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_2_trusted.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_3_trusted.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_1.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_2.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_3.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_4.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_5.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_semantic_this_1.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_semantic_this_2.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_semantic_this_3.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_array_struct_trusted_1.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_array_struct_trusted_2.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_array_trusted_1.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_array_trusted_2.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_struct_trusted_1.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_struct_trusted_2.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_struct_trusted_3.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_struct_trusted_4.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_array_struct_trusted_1.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_array_struct_trusted_2.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_array_trusted_1.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_array_trusted_2.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_struct_trusted_1.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_struct_trusted_2.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_struct_trusted_3.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_struct_trusted_4.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_pure_trusted.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_2_trusted.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_indirect_trusted.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_trusted.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_unsafe_trusted.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_trusted.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_unsafe_trusted.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/token_trusted_transfer_correct.sol create mode 100644 test/libsolidity/smtCheckerTests/external_calls/token_trusted_transfer_wrong.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/getters/external_getter_1.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/getters/external_getter_2.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/getters/external_getter_this_1.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/getters/external_getter_this_2.sol diff --git a/Changelog.md b/Changelog.md index 7619fbd40db2..5ce4057a4800 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Language Features: Compiler Features: + * SMTChecker: New trusted mode that assumes that any compile-time available code is the actual used code even in external calls. This can be used via the CLI option ``--model-checker-ext-calls trusted`` or the JSON field ``settings.modelChecker.extCalls: "trusted"``. Bugfixes: diff --git a/docs/smtchecker.rst b/docs/smtchecker.rst index c16a3e5d71dd..276f9c33a00b 100644 --- a/docs/smtchecker.rst +++ b/docs/smtchecker.rst @@ -518,6 +518,188 @@ which has the following form: "source2.sol": ["contract2", "contract3"] } +Trusted External Calls +====================== + +By default, the SMTChecker does not assume that compile-time available code +is the same as the runtime code for external calls. Take the following contracts +as an example: + +.. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.8.0; + + contract Ext { + uint public x; + function setX(uint _x) public { x = _x; } + } + contract MyContract { + function callExt(Ext _e) public { + _e.setX(42); + assert(_e.x() == 42); + } + } + +When ``MyContract.callExt`` is called, an address is given as the argument. +At deployment time, we cannot know for sure that address ``_e`` actually +contains a deployment of contract ``Ext``. +Therefore, the SMTChecker will warn that the assertion above can be violated, +which is true, if ``_e`` contains another contract than ``Ext``. + +However, it can be useful to treat these external calls as trusted, for example, +to test that different implementations of an interface conform to the same property. +This means assuming that address ``_e`` indeed was deployed as contract ``Ext``. +This mode can be enabled via the CLI option ``--model-checker-ext-calls=trusted`` +or the JSON field ``settings.modelChecker.extCalls: "trusted"``. + +Please be aware that enabling this mode can make the SMTChecker analysis much more +computationally costly. + +An important part of this mode is that it is applied to contract types and high +level external calls to contracts, and not low level calls such as ``call`` and +``delegatecall``. The storage of an address is stored per contract type, and +the SMTChecker assumes that an externally called contract has the type of the +caller expression. Therefore, casting an ``address`` or a contract to +different contract types will yield different storage values and can give +unsound results if the assumptions are inconsistent, such as the example below: + +.. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.8.0; + + contract D { + constructor(uint _x) { x = _x; } + uint public x; + function setX(uint _x) public { x = _x; } + } + + contract E { + constructor() { x = 2; } + uint public x; + function setX(uint _x) public { x = _x; } + } + + contract C { + function f() public { + address d = address(new D(42)); + + // `d` was deployed as `D`, so its `x` should be 42 now. + assert(D(d).x() == 42); // should hold + assert(D(d).x() == 43); // should fail + + // E and D have the same interface, so the following + // call would also work at runtime. + // However, the change to `E(d)` is not reflected in `D(d)`. + E(d).setX(1024); + + // Reading from `D(d)` now will show old values. + // The assertion below should fail at runtime, + // but succeeds in this mode's analysis (unsound). + assert(D(d).x() == 42); + // The assertion below should succeed at runtime, + // but fails in this mode's analysis (false positive). + assert(D(d).x() == 1024); + } + } + +Due to the above, make sure that the trusted external calls to a certain +variable of ``address`` or ``contract`` type always have the same caller +expression type. + +It is also helpful to cast the called contract's variable as the type of the +most derived type in case of inheritance. + + .. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.8.0; + + interface Token { + function balanceOf(address _a) external view returns (uint); + function transfer(address _to, uint _amt) external; + } + + contract TokenCorrect is Token { + mapping (address => uint) balance; + constructor(address _a, uint _b) { + balance[_a] = _b; + } + function balanceOf(address _a) public view override returns (uint) { + return balance[_a]; + } + function transfer(address _to, uint _amt) public override { + require(balance[msg.sender] >= _amt); + balance[msg.sender] -= _amt; + balance[_to] += _amt; + } + } + + contract Test { + function property_transfer(address _token, address _to, uint _amt) public { + require(_to != address(this)); + + TokenCorrect t = TokenCorrect(_token); + + uint xPre = t.balanceOf(address(this)); + require(xPre >= _amt); + uint yPre = t.balanceOf(_to); + + t.transfer(_to, _amt); + uint xPost = t.balanceOf(address(this)); + uint yPost = t.balanceOf(_to); + + assert(xPost == xPre - _amt); + assert(yPost == yPre + _amt); + } + } + +Note that in function ``property_transfer``, the external calls are +performed on variable ``t`` + +Another caveat of this mode are calls to state variables of contract type +outside the analyzed contract. In the code below, even though ``B`` deploys +``A``, it is also possible for the address stored in ``B.a`` to be called by +anyone outside of ``B`` in between transactions to ``B`` itself. To reflect the +possible changes to ``B.a``, the encoding allows an unbounded number of calls +to be made to ``B.a`` externally. The encoding will keep track of ``B.a``'s +storage, therefore assertion (2) should hold. However, currently the encoding +allows such calls to be made from ``B`` conceptually, therefore assertion (3) +fails. Making the encoding stronger logically is an extension of the trusted +mode and is under development. Note that the encoding does not keep track of +storage for ``address`` variables, therefore if ``B.a`` had type ``address`` +the encoding would assume that its storage does not change in between +transactions to ``B``. + + .. code-block:: solidity + + pragma solidity >=0.8.0; + + contract A { + uint public x; + address immutable public owner; + constructor() { + owner = msg.sender; + } + function setX(uint _x) public { + require(msg.sender == owner); + x = _x; + } + } + + contract B { + A a; + constructor() { + a = new A(); + assert(a.x() == 0); // (1) should hold + } + function g() public view { + assert(a.owner() == address(this)); // (2) should hold + assert(a.x() == 0); // (3) should hold, but fails due to a false positive + } + } + Reported Inferred Inductive Invariants ====================================== diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 6d6264e1aa66..251a2c053696 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -433,6 +433,10 @@ Input Description "divModNoSlacks": false, // Choose which model checker engine to use: all (default), bmc, chc, none. "engine": "chc", + // Choose whether external calls should be considered trusted in case the + // code of the called function is available at compile-time. + // For details see the SMTChecker section. + "extCalls": "trusted", // Choose which types of invariants should be reported to the user: contract, reentrancy. "invariants": ["contract", "reentrancy"], // Choose whether to output all unproved targets. The default is `false`. diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index 0b4f347816a5..d17b5c93a9c5 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -82,7 +82,7 @@ void BMC::analyze(SourceUnit const& _source, map(*_funCall.expression().annotation().type); if (funType.kind() == FunctionType::Kind::External) - return isTrustedExternalCall(&_funCall.expression()); + return isExternalCallToThis(&_funCall.expression()); else if (funType.kind() != FunctionType::Kind::Internal) return false; @@ -567,7 +567,7 @@ void BMC::internalOrExternalFunctionCall(FunctionCall const& _funCall) auto const& funType = dynamic_cast(*_funCall.expression().annotation().type); if (shouldInlineFunctionCall(_funCall, currentScopeContract(), m_currentContract)) inlineFunctionCall(_funCall); - else if (isPublicGetter(_funCall.expression())) + else if (publicGetter(_funCall.expression())) { // Do nothing here. // The processing happens in SMT Encoder, but we need to prevent the resetting of the state variables. diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 83602ace346b..db67426259c2 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -104,7 +104,7 @@ void CHC::analyze(SourceUnit const& _source) auto sources = sourceDependencies(_source); collectFreeFunctions(sources); createFreeConstants(sources); - state().prepareForSourceUnit(_source); + state().prepareForSourceUnit(_source, encodeExternalCallsAsTrusted()); for (auto const* source: sources) defineInterfacesAndSummaries(*source); @@ -175,15 +175,30 @@ void CHC::endVisit(ContractDefinition const& _contract) smtutil::Expression zeroes(true); for (auto var: stateVariablesIncludingInheritedAndPrivate(_contract)) zeroes = zeroes && currentValue(*var) == smt::zeroValue(var->type()); + + smtutil::Expression newAddress = encodeExternalCallsAsTrusted() ? + !state().addressActive(state().thisAddress()) : + smtutil::Expression(true); + // The contract's address might already have funds before deployment, // so the balance must be at least `msg.value`, but not equals. auto initialBalanceConstraint = state().balance(state().thisAddress()) >= state().txMember("msg.value"); addRule(smtutil::Expression::implies( - initialConstraints(_contract) && zeroes && initialBalanceConstraint, + initialConstraints(_contract) && zeroes && newAddress && initialBalanceConstraint, predicate(entry) ), entry.functor().name); + setCurrentBlock(entry); + if (encodeExternalCallsAsTrusted()) + { + auto const& entryAfterAddress = *createConstructorBlock(_contract, "implicit_constructor_entry_after_address"); + state().setAddressActive(state().thisAddress(), true); + + connectBlocks(m_currentBlock, predicate(entryAfterAddress)); + setCurrentBlock(entryAfterAddress); + } + solAssert(!m_errorDest, ""); m_errorDest = m_constructorSummaries.at(&_contract); // We need to evaluate the base constructor calls (arguments) from derived -> base @@ -220,6 +235,9 @@ void CHC::endVisit(ContractDefinition const& _contract) m_context.addAssertion(errorFlag().currentValue() == 0); } + if (encodeExternalCallsAsTrusted()) + state().writeStateVars(_contract, state().thisAddress()); + connectBlocks(m_currentBlock, summary(_contract)); setCurrentBlock(*m_constructorSummaries.at(&_contract)); @@ -550,10 +568,12 @@ void CHC::endVisit(FunctionCall const& _funCall) externalFunctionCall(_funCall); SMTEncoder::endVisit(_funCall); break; + case FunctionType::Kind::Creation: + visitDeployment(_funCall); + break; case FunctionType::Kind::DelegateCall: case FunctionType::Kind::BareCallCode: case FunctionType::Kind::BareDelegateCall: - case FunctionType::Kind::Creation: SMTEncoder::endVisit(_funCall); unknownFunctionCall(_funCall); break; @@ -717,6 +737,19 @@ void CHC::visitAssert(FunctionCall const& _funCall) verificationTargetEncountered(&_funCall, VerificationTargetType::Assert, errorCondition); } +void CHC::visitPublicGetter(FunctionCall const& _funCall) +{ + createExpr(_funCall); + if (encodeExternalCallsAsTrusted()) + { + auto const& access = dynamic_cast(_funCall.expression()); + auto const& contractType = dynamic_cast(*access.expression().annotation().type); + state().writeStateVars(*m_currentContract, state().thisAddress()); + state().readStateVars(contractType.contractDefinition(), expr(access.expression())); + } + SMTEncoder::visitPublicGetter(_funCall); +} + void CHC::visitAddMulMod(FunctionCall const& _funCall) { solAssert(_funCall.arguments().at(2), ""); @@ -726,6 +759,66 @@ void CHC::visitAddMulMod(FunctionCall const& _funCall) SMTEncoder::visitAddMulMod(_funCall); } +void CHC::visitDeployment(FunctionCall const& _funCall) +{ + if (!encodeExternalCallsAsTrusted()) + { + SMTEncoder::endVisit(_funCall); + unknownFunctionCall(_funCall); + return; + } + + auto [callExpr, callOptions] = functionCallExpression(_funCall); + auto funType = dynamic_cast(callExpr->annotation().type); + ContractDefinition const* contract = + &dynamic_cast(*funType->returnParameterTypes().front()).contractDefinition(); + + // copy state variables from m_currentContract to state.storage. + state().writeStateVars(*m_currentContract, state().thisAddress()); + errorFlag().increaseIndex(); + + Expression const* value = valueOption(callOptions); + if (value) + decreaseBalanceFromOptionsValue(*value); + + auto originalTx = state().tx(); + newTxConstraints(value); + + auto prevThisAddr = state().thisAddress(); + auto newAddr = state().newThisAddress(); + + if (auto constructor = contract->constructor()) + { + auto const& args = _funCall.sortedArguments(); + auto const& params = constructor->parameters(); + solAssert(args.size() == params.size(), ""); + for (auto [arg, param]: ranges::zip_view(args, params)) + m_context.addAssertion(expr(*arg) == m_context.variable(*param)->currentValue()); + } + for (auto var: stateVariablesIncludingInheritedAndPrivate(*contract)) + m_context.variable(*var)->increaseIndex(); + Predicate const& constructorSummary = *m_constructorSummaries.at(contract); + m_context.addAssertion(smt::constructorCall(constructorSummary, m_context, false)); + + solAssert(m_errorDest, ""); + connectBlocks( + m_currentBlock, + predicate(*m_errorDest), + errorFlag().currentValue() > 0 + ); + m_context.addAssertion(errorFlag().currentValue() == 0); + + m_context.addAssertion(state().newThisAddress() == prevThisAddr); + + // copy state variables from state.storage to m_currentContract. + state().readStateVars(*m_currentContract, state().thisAddress()); + + state().newTx(); + m_context.addAssertion(originalTx == state().tx()); + + defineExpr(_funCall, newAddr); +} + void CHC::internalFunctionCall(FunctionCall const& _funCall) { solAssert(m_currentContract, ""); @@ -750,6 +843,53 @@ void CHC::internalFunctionCall(FunctionCall const& _funCall) m_context.addAssertion(errorFlag().currentValue() == 0); } +void CHC::addNondetCalls(ContractDefinition const& _contract) +{ + for (auto var: _contract.stateVariables()) + if (auto contractType = dynamic_cast(var->type())) + { + auto const& symbVar = m_context.variable(*var); + m_context.addAssertion(symbVar->currentValue() == symbVar->valueAtIndex(0)); + nondetCall(contractType->contractDefinition(), *var); + } +} + +void CHC::nondetCall(ContractDefinition const& _contract, VariableDeclaration const& _var) +{ + auto address = m_context.variable(_var)->currentValue(); + // Load the called contract's state variables from the global state. + state().readStateVars(_contract, address); + + m_context.addAssertion(state().state() == state().state(0)); + auto preCallState = vector{state().state()} + currentStateVariables(_contract); + + state().newState(); + for (auto const* var: _contract.stateVariables()) + m_context.variable(*var)->increaseIndex(); + + auto error = errorFlag().increaseIndex(); + + Predicate const& callPredicate = *createSymbolicBlock( + nondetInterfaceSort(_contract, state()), + "nondet_call_" + uniquePrefix(), + PredicateType::FunctionSummary, + &_var, + m_currentContract + ); + auto postCallState = vector{state().state()} + currentStateVariables(_contract); + vector stateExprs{error, address, state().abi(), state().crypto()}; + + auto nondet = (*m_nondetInterfaces.at(&_contract))(stateExprs + preCallState + postCallState); + auto nondetCall = callPredicate(stateExprs + preCallState + postCallState); + + addRule(smtutil::Expression::implies(nondet, nondetCall), nondetCall.name); + + m_context.addAssertion(nondetCall); + + // Load the called contract's state variables into the global state. + state().writeStateVars(_contract, address); +} + void CHC::externalFunctionCall(FunctionCall const& _funCall) { /// In external function calls we do not add a "predicate call" @@ -757,15 +897,10 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall) /// so we just add the nondet_interface predicate. solAssert(m_currentContract, ""); - auto [callExpr, callOptions] = functionCallExpression(_funCall); - - if (isTrustedExternalCall(callExpr)) - { - externalFunctionCallToTrustedCode(_funCall); - return; - } + auto [callExpr, callOptions] = functionCallExpression(_funCall); FunctionType const& funType = dynamic_cast(*callExpr->annotation().type); + auto kind = funType.kind(); solAssert( kind == FunctionType::Kind::External || @@ -774,37 +909,42 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall) "" ); - bool usesStaticCall = kind == FunctionType::Kind::BareStaticCall; - solAssert(m_currentContract, ""); + // Only consider high level external calls in trusted mode. + if ( + kind == FunctionType::Kind::External && + (encodeExternalCallsAsTrusted() || isExternalCallToThis(callExpr)) + ) + { + externalFunctionCallToTrustedCode(_funCall); + return; + } + + // Low level calls are still encoded nondeterministically. + auto function = functionCallToDefinition(_funCall, currentScopeContract(), m_currentContract); if (function) - { - usesStaticCall |= function->stateMutability() == StateMutability::Pure || - function->stateMutability() == StateMutability::View; for (auto var: function->returnParameters()) m_context.variable(*var)->increaseIndex(); - } + // If we see a low level call in trusted mode, + // we need to havoc the global state. + if ( + kind == FunctionType::Kind::BareCall && + encodeExternalCallsAsTrusted() + ) + state().newStorage(); + + // No reentrancy from constructor calls. if (!m_currentFunction || m_currentFunction->isConstructor()) return; - if (callOptions) - { - optional valueIndex; - for (auto&& [i, name]: callOptions->names() | ranges::views::enumerate) - if (name && *name == "value") - { - valueIndex = i; - break; - } - if (valueIndex) - state().addBalance(state().thisAddress(), 0 - expr(*callOptions->options().at(*valueIndex))); - } + if (Expression const* value = valueOption(callOptions)) + decreaseBalanceFromOptionsValue(*value); auto preCallState = vector{state().state()} + currentStateVariables(); - if (!usesStaticCall) + if (!usesStaticCall(_funCall)) { state().newState(); for (auto const* var: m_stateVariables) @@ -843,8 +983,14 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall) void CHC::externalFunctionCallToTrustedCode(FunctionCall const& _funCall) { + if (publicGetter(_funCall.expression())) + visitPublicGetter(_funCall); + solAssert(m_currentContract, ""); - FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); + + auto [callExpr, callOptions] = functionCallExpression(_funCall); + FunctionType const& funType = dynamic_cast(*callExpr->annotation().type); + auto kind = funType.kind(); solAssert(kind == FunctionType::Kind::External || kind == FunctionType::Kind::BareStaticCall, ""); @@ -854,14 +1000,25 @@ void CHC::externalFunctionCallToTrustedCode(FunctionCall const& _funCall) // External call creates a new transaction. auto originalTx = state().tx(); - auto txOrigin = state().txMember("tx.origin"); - state().newTx(); - // set the transaction sender as this contract - m_context.addAssertion(state().txMember("msg.sender") == state().thisAddress()); - // set the transaction value as 0 - m_context.addAssertion(state().txMember("msg.value") == 0); - // set the origin to be the current transaction origin - m_context.addAssertion(state().txMember("tx.origin") == txOrigin); + Expression const* value = valueOption(callOptions); + newTxConstraints(value); + + auto calledAddress = contractAddressValue(_funCall); + if (value) + { + decreaseBalanceFromOptionsValue(*value); + state().addBalance(calledAddress, expr(*value)); + } + + if (encodeExternalCallsAsTrusted()) + { + // The order here is important!! Write should go first. + + // Load the caller contract's state variables into the global state. + state().writeStateVars(*m_currentContract, state().thisAddress()); + // Load the called contract's state variables from the global state. + state().readStateVars(*function->annotation().contract, contractAddressValue(_funCall)); + } smtutil::Expression pred = predicate(_funCall); @@ -878,6 +1035,17 @@ void CHC::externalFunctionCallToTrustedCode(FunctionCall const& _funCall) (errorFlag().currentValue() > 0) ); m_context.addAssertion(errorFlag().currentValue() == 0); + + if (!usesStaticCall(_funCall)) + if (encodeExternalCallsAsTrusted()) + { + // The order here is important!! Write should go first. + + // Load the called contract's state variables into the global state. + state().writeStateVars(*function->annotation().contract, contractAddressValue(_funCall)); + // Load the caller contract's state variables from the global state. + state().readStateVars(*m_currentContract, state().thisAddress()); + } } void CHC::unknownFunctionCall(FunctionCall const&) @@ -1088,6 +1256,14 @@ set CHC::transactionVerificationTargetsIds(ASTNode const* _txRoot) return verificationTargetsIds; } +bool CHC::usesStaticCall(FunctionCall const& _funCall) +{ + FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); + auto kind = funType.kind(); + auto function = functionCallToDefinition(_funCall, currentScopeContract(), m_currentContract); + return (function && (function->stateMutability() == StateMutability::Pure || function->stateMutability() == StateMutability::View)) || kind == FunctionType::Kind::BareStaticCall; +} + optional CHC::natspecOptionFromString(string const& _option) { static map options{ @@ -1128,6 +1304,11 @@ SortPointer CHC::sort(FunctionDefinition const& _function) return functionBodySort(_function, m_currentContract, state()); } +bool CHC::encodeExternalCallsAsTrusted() +{ + return m_settings.externalCalls.isTrusted(); +} + SortPointer CHC::sort(ASTNode const* _node) { if (auto funDef = dynamic_cast(_node)) @@ -1232,6 +1413,62 @@ void CHC::defineExternalFunctionInterface(FunctionDefinition const& _function, C m_context.addAssertion(smt::symbolicUnknownConstraints(state().balance(state().thisAddress()) + k.currentValue(), TypeProvider::uint256())); state().addBalance(state().thisAddress(), k.currentValue()); + if (encodeExternalCallsAsTrusted()) + { + // If the contract has state variables that are addresses to other contracts, + // we need to encode the fact that those contracts may have been called in between + // transactions to _contract. + // + // We do that by adding nondet_interface constraints for those contracts, + // in the last line of this if block. + // + // If there are state variables of container types like structs or arrays + // that indirectly contain contract types, we havoc the state for simplicity, + // in the first part of this block. + // TODO: This could actually be supported. + // For structs: simply collect the SMT expressions of all the indirect contract type members. + // For arrays: more involved, needs to traverse the array symbolically and do the same for each contract. + // For mappings: way more complicated if the element type is a contract. + auto hasContractOrAddressSubType = [&](VariableDeclaration const* _var) -> bool { + bool foundContract = false; + solidity::util::BreadthFirstSearch bfs{{_var->type()}}; + bfs.run([&](auto _type, auto&& _addChild) { + if ( + _type->category() == Type::Category::Address || + _type->category() == Type::Category::Contract + ) + { + foundContract = true; + bfs.abort(); + } + if (auto const* mapType = dynamic_cast(_type)) + _addChild(mapType->valueType()); + else if (auto const* arrayType = dynamic_cast(_type)) + _addChild(arrayType->baseType()); + else if (auto const* structType = dynamic_cast(_type)) + for (auto const& member: structType->nativeMembers(nullptr)) + _addChild(member.type); + }); + return foundContract; + }; + bool found = false; + for (auto var: m_currentContract->stateVariables()) + if ( + var->type()->category() != Type::Category::Address && + var->type()->category() != Type::Category::Contract && + hasContractOrAddressSubType(var) + ) + { + found = true; + break; + } + + if (found) + state().newStorage(); + else + addNondetCalls(*m_currentContract); + } + errorFlag().increaseIndex(); m_context.addAssertion(summaryCall(_function)); @@ -1308,26 +1545,26 @@ smtutil::Expression CHC::summary(FunctionDefinition const& _function, ContractDe return smt::function(*m_summaries.at(&_contract).at(&_function), &_contract, m_context); } -smtutil::Expression CHC::summaryCall(FunctionDefinition const& _function, ContractDefinition const& _contract) +smtutil::Expression CHC::summary(FunctionDefinition const& _function) { - return smt::functionCall(*m_summaries.at(&_contract).at(&_function), &_contract, m_context); + solAssert(m_currentContract, ""); + return summary(_function, *m_currentContract); } -smtutil::Expression CHC::externalSummary(FunctionDefinition const& _function, ContractDefinition const& _contract) +smtutil::Expression CHC::summaryCall(FunctionDefinition const& _function, ContractDefinition const& _contract) { - return smt::function(*m_externalSummaries.at(&_contract).at(&_function), &_contract, m_context); + return smt::functionCall(*m_summaries.at(&_contract).at(&_function), &_contract, m_context); } -smtutil::Expression CHC::summary(FunctionDefinition const& _function) +smtutil::Expression CHC::summaryCall(FunctionDefinition const& _function) { solAssert(m_currentContract, ""); - return summary(_function, *m_currentContract); + return summaryCall(_function, *m_currentContract); } -smtutil::Expression CHC::summaryCall(FunctionDefinition const& _function) +smtutil::Expression CHC::externalSummary(FunctionDefinition const& _function, ContractDefinition const& _contract) { - solAssert(m_currentContract, ""); - return summaryCall(_function, *m_currentContract); + return smt::function(*m_externalSummaries.at(&_contract).at(&_function), &_contract, m_context); } smtutil::Expression CHC::externalSummary(FunctionDefinition const& _function) @@ -1516,18 +1753,19 @@ smtutil::Expression CHC::predicate(FunctionCall const& _funCall) auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts; solAssert(kind != FunctionType::Kind::Internal || function->isFree() || (contract && contract->isLibrary()) || util::contains(hierarchy, contract), ""); - bool usesStaticCall = function->stateMutability() == StateMutability::Pure || function->stateMutability() == StateMutability::View; + if (kind == FunctionType::Kind::Internal) + contract = m_currentContract; - args += currentStateVariables(*m_currentContract); - args += symbolicArguments(_funCall, m_currentContract); - if (!m_currentContract->isLibrary() && !usesStaticCall) + args += currentStateVariables(*contract); + args += symbolicArguments(_funCall, contract); + if (!usesStaticCall(_funCall)) { state().newState(); - for (auto const& var: m_stateVariables) + for (auto const& var: stateVariablesIncludingInheritedAndPrivate(*contract)) m_context.variable(*var)->increaseIndex(); } args += vector{state().state()}; - args += currentStateVariables(*m_currentContract); + args += currentStateVariables(*contract); for (auto var: function->parameters() + function->returnParameters()) { @@ -1538,14 +1776,14 @@ smtutil::Expression CHC::predicate(FunctionCall const& _funCall) args.push_back(currentValue(*var)); } - Predicate const& summary = *m_summaries.at(m_currentContract).at(function); - auto from = smt::function(summary, m_currentContract, m_context); + Predicate const& summary = *m_summaries.at(contract).at(function); + auto from = smt::function(summary, contract, m_context); Predicate const& callPredicate = *createSummaryBlock( *function, - *m_currentContract, + *contract, kind == FunctionType::Kind::Internal ? PredicateType::InternalCall : PredicateType::ExternalCallTrusted ); - auto to = smt::function(callPredicate, m_currentContract, m_context); + auto to = smt::function(callPredicate, contract, m_context); addRule(smtutil::Expression::implies(from, to), to.name); return callPredicate(args); @@ -1910,60 +2148,63 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& Predicate const* summaryPredicate = Predicate::predicate(summaryNode.name); auto const& summaryArgs = summaryNode.arguments; - auto stateVars = summaryPredicate->stateVariables(); - solAssert(stateVars.has_value(), ""); - auto stateValues = summaryPredicate->summaryStateValues(summaryArgs); - solAssert(stateValues.size() == stateVars->size(), ""); - - if (first) + if (!summaryPredicate->programVariable()) { - first = false; - /// Generate counterexample message local to the failed target. - localState = formatVariableModel(*stateVars, stateValues, ", ") + "\n"; + auto stateVars = summaryPredicate->stateVariables(); + solAssert(stateVars.has_value(), ""); + auto stateValues = summaryPredicate->summaryStateValues(summaryArgs); + solAssert(stateValues.size() == stateVars->size(), ""); - if (auto calledFun = summaryPredicate->programFunction()) + if (first) { - auto inValues = summaryPredicate->summaryPostInputValues(summaryArgs); - auto const& inParams = calledFun->parameters(); - if (auto inStr = formatVariableModel(inParams, inValues, "\n"); !inStr.empty()) - localState += inStr + "\n"; - auto outValues = summaryPredicate->summaryPostOutputValues(summaryArgs); - auto const& outParams = calledFun->returnParameters(); - if (auto outStr = formatVariableModel(outParams, outValues, "\n"); !outStr.empty()) - localState += outStr + "\n"; - - optional localErrorId; - solidity::util::BreadthFirstSearch bfs{{summaryId}}; - bfs.run([&](auto _nodeId, auto&& _addChild) { - auto const& children = _graph.edges.at(_nodeId); - if ( - children.size() == 1 && - nodePred(children.front())->isFunctionErrorBlock() - ) - { - localErrorId = children.front(); - bfs.abort(); - } - ranges::for_each(children, _addChild); - }); + first = false; + /// Generate counterexample message local to the failed target. + localState = formatVariableModel(*stateVars, stateValues, ", ") + "\n"; - if (localErrorId.has_value()) + if (auto calledFun = summaryPredicate->programFunction()) { - auto const* localError = nodePred(*localErrorId); - solAssert(localError && localError->isFunctionErrorBlock(), ""); - auto const [localValues, localVars] = localError->localVariableValues(nodeArgs(*localErrorId)); - if (auto localStr = formatVariableModel(localVars, localValues, "\n"); !localStr.empty()) - localState += localStr + "\n"; + auto inValues = summaryPredicate->summaryPostInputValues(summaryArgs); + auto const& inParams = calledFun->parameters(); + if (auto inStr = formatVariableModel(inParams, inValues, "\n"); !inStr.empty()) + localState += inStr + "\n"; + auto outValues = summaryPredicate->summaryPostOutputValues(summaryArgs); + auto const& outParams = calledFun->returnParameters(); + if (auto outStr = formatVariableModel(outParams, outValues, "\n"); !outStr.empty()) + localState += outStr + "\n"; + + optional localErrorId; + solidity::util::BreadthFirstSearch bfs{{summaryId}}; + bfs.run([&](auto _nodeId, auto&& _addChild) { + auto const& children = _graph.edges.at(_nodeId); + if ( + children.size() == 1 && + nodePred(children.front())->isFunctionErrorBlock() + ) + { + localErrorId = children.front(); + bfs.abort(); + } + ranges::for_each(children, _addChild); + }); + + if (localErrorId.has_value()) + { + auto const* localError = nodePred(*localErrorId); + solAssert(localError && localError->isFunctionErrorBlock(), ""); + auto const [localValues, localVars] = localError->localVariableValues(nodeArgs(*localErrorId)); + if (auto localStr = formatVariableModel(localVars, localValues, "\n"); !localStr.empty()) + localState += localStr + "\n"; + } } } - } - else - { - auto modelMsg = formatVariableModel(*stateVars, stateValues, ", "); - /// We report the state after every tx in the trace except for the last, which is reported - /// first in the code above. - if (!modelMsg.empty()) - path.emplace_back("State: " + modelMsg); + else + { + auto modelMsg = formatVariableModel(*stateVars, stateValues, ", "); + /// We report the state after every tx in the trace except for the last, which is reported + /// first in the code above. + if (!modelMsg.empty()) + path.emplace_back("State: " + modelMsg); + } } string txCex = summaryPredicate->formatSummaryCall(summaryArgs, m_charStreamProvider); @@ -1992,6 +2233,12 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& if (calls.size() > callTraceSize + 1) calls.front() += ", synthesized as:"; } + else if (pred->programVariable()) + { + calls.front() += "-- action on external contract in state variable \"" + pred->programVariable()->name() + "\""; + if (calls.size() > callTraceSize + 1) + calls.front() += ", synthesized as:"; + } else if (pred->isFunctionSummary() && parentPred->isExternalCallUntrusted()) calls.front() += " -- reentrant call"; }; @@ -2047,7 +2294,8 @@ map> CHC::summaryCalls(CHCSolverInterface::CexGraph c nodePred->isInternalCall() || nodePred->isExternalCallTrusted() || nodePred->isExternalCallUntrusted() || - rootPred->isExternalCallUntrusted() + rootPred->isExternalCallUntrusted() || + rootPred->programVariable() )) { calls[root].push_back(node); @@ -2105,3 +2353,31 @@ SymbolicIntVariable& CHC::errorFlag() { return state().errorFlag(); } + +void CHC::newTxConstraints(Expression const* _value) +{ + auto txOrigin = state().txMember("tx.origin"); + state().newTx(); + // set the transaction sender as this contract + m_context.addAssertion(state().txMember("msg.sender") == state().thisAddress()); + // set the origin to be the current transaction origin + m_context.addAssertion(state().txMember("tx.origin") == txOrigin); + + if (_value) + // set the msg value + m_context.addAssertion(state().txMember("msg.value") == expr(*_value)); +} + +frontend::Expression const* CHC::valueOption(FunctionCallOptions const* _options) +{ + if (_options) + for (auto&& [i, name]: _options->names() | ranges::views::enumerate) + if (name && *name == "value") + return _options->options().at(i).get(); + return nullptr; +} + +void CHC::decreaseBalanceFromOptionsValue(Expression const& _value) +{ + state().addBalance(state().thisAddress(), 0 - expr(_value)); +} diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 8b81ffb59a5e..c113f85831b9 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -110,10 +110,14 @@ class CHC: public SMTEncoder void popInlineFrame(CallableDeclaration const& _callable) override; void visitAssert(FunctionCall const& _funCall); + void visitPublicGetter(FunctionCall const& _funCall) override; void visitAddMulMod(FunctionCall const& _funCall) override; + void visitDeployment(FunctionCall const& _funCall); void internalFunctionCall(FunctionCall const& _funCall); void externalFunctionCall(FunctionCall const& _funCall); void externalFunctionCallToTrustedCode(FunctionCall const& _funCall); + void addNondetCalls(ContractDefinition const& _contract); + void nondetCall(ContractDefinition const& _contract, VariableDeclaration const& _var); void unknownFunctionCall(FunctionCall const& _funCall); void makeArrayPopVerificationTarget(FunctionCall const& _arrayPop) override; void makeOutOfBoundsVerificationTarget(IndexAccess const& _access) override; @@ -135,6 +139,7 @@ class CHC: public SMTEncoder void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr) override; void setCurrentBlock(Predicate const& _block); std::set transactionVerificationTargetsIds(ASTNode const* _txRoot); + bool usesStaticCall(FunctionCall const& _funCall); //@} /// SMT Natspec and abstraction helpers. @@ -148,6 +153,10 @@ class CHC: public SMTEncoder /// @returns true if _function is Natspec annotated to be abstracted by /// nondeterministic values. bool abstractAsNondet(FunctionDefinition const& _function); + + /// @returns true if external calls should be considered trusted. + /// If that's the case, their code is used if available at compile time. + bool encodeExternalCallsAsTrusted(); //@} /// Sort helpers. @@ -310,6 +319,20 @@ class CHC: public SMTEncoder unsigned newErrorId(); smt::SymbolicIntVariable& errorFlag(); + + /// Adds to the solver constraints that + /// - propagate tx.origin + /// - set the current contract as msg.sender + /// - set the msg.value as _value, if not nullptr + void newTxConstraints(Expression const* _value); + + /// @returns the expression representing the value sent in + /// an external call if present, + /// and nullptr otherwise. + frontend::Expression const* valueOption(FunctionCallOptions const* _options); + + /// Adds constraints that decrease the balance of the caller by _value. + void decreaseBalanceFromOptionsValue(Expression const& _value); //@} /// Predicates. diff --git a/libsolidity/formal/ModelCheckerSettings.cpp b/libsolidity/formal/ModelCheckerSettings.cpp index 274571a15466..d1e383d63f25 100644 --- a/libsolidity/formal/ModelCheckerSettings.cpp +++ b/libsolidity/formal/ModelCheckerSettings.cpp @@ -130,3 +130,12 @@ std::optional ModelCheckerContracts::fromString(string co return ModelCheckerContracts{chosen}; } + +std::optional ModelCheckerExtCalls::fromString(string const& _mode) +{ + if (_mode == "untrusted") + return ModelCheckerExtCalls{Mode::UNTRUSTED}; + if (_mode == "trusted") + return ModelCheckerExtCalls{Mode::TRUSTED}; + return {}; +} diff --git a/libsolidity/formal/ModelCheckerSettings.h b/libsolidity/formal/ModelCheckerSettings.h index 2c86993b9d31..3c1f623a0720 100644 --- a/libsolidity/formal/ModelCheckerSettings.h +++ b/libsolidity/formal/ModelCheckerSettings.h @@ -140,6 +140,21 @@ struct ModelCheckerTargets std::set targets; }; +struct ModelCheckerExtCalls +{ + enum class Mode + { + UNTRUSTED, + TRUSTED + }; + + Mode mode = Mode::UNTRUSTED; + + static std::optional fromString(std::string const& _mode); + + bool isTrusted() const { return mode == Mode::TRUSTED; } +}; + struct ModelCheckerSettings { ModelCheckerContracts contracts = ModelCheckerContracts::Default(); @@ -151,6 +166,7 @@ struct ModelCheckerSettings /// might prefer the precise encoding. bool divModNoSlacks = false; ModelCheckerEngine engine = ModelCheckerEngine::None(); + ModelCheckerExtCalls externalCalls = {}; ModelCheckerInvariants invariants = ModelCheckerInvariants::Default(); bool showUnproved = false; smtutil::SMTSolverChoice solvers = smtutil::SMTSolverChoice::Z3(); @@ -164,6 +180,7 @@ struct ModelCheckerSettings contracts == _other.contracts && divModNoSlacks == _other.divModNoSlacks && engine == _other.engine && + externalCalls.mode == _other.externalCalls.mode && invariants == _other.invariants && showUnproved == _other.showUnproved && solvers == _other.solvers && diff --git a/libsolidity/formal/Predicate.cpp b/libsolidity/formal/Predicate.cpp index 918728d86de8..7119a59e8ba8 100644 --- a/libsolidity/formal/Predicate.cpp +++ b/libsolidity/formal/Predicate.cpp @@ -144,6 +144,11 @@ FunctionCall const* Predicate::programFunctionCall() const return dynamic_cast(m_node); } +VariableDeclaration const* Predicate::programVariable() const +{ + return dynamic_cast(m_node); +} + optional> Predicate::stateVariables() const { if (m_contractContext) @@ -214,6 +219,9 @@ string Predicate::formatSummaryCall( { solAssert(isSummary(), ""); + if (programVariable()) + return {}; + if (auto funCall = programFunctionCall()) { if (funCall->location().hasText()) @@ -348,6 +356,8 @@ vector> Predicate::summaryStateValues(vector(stateVars->size()); stateLast = stateFirst + static_cast(stateVars->size()); } + else if (programVariable()) + return {}; else solAssert(false, ""); diff --git a/libsolidity/formal/Predicate.h b/libsolidity/formal/Predicate.h index 16af42f45fe9..d7ac0da35223 100644 --- a/libsolidity/formal/Predicate.h +++ b/libsolidity/formal/Predicate.h @@ -115,6 +115,10 @@ class Predicate /// or nullptr otherwise. FunctionCall const* programFunctionCall() const; + /// @returns the VariableDeclaration that this predicate represents + /// or nullptr otherwise. + VariableDeclaration const* programVariable() const; + /// @returns the program state variables in the scope of this predicate. std::optional> stateVariables() const; diff --git a/libsolidity/formal/PredicateInstance.cpp b/libsolidity/formal/PredicateInstance.cpp index aff6aa19b43d..5e057efe3745 100644 --- a/libsolidity/formal/PredicateInstance.cpp +++ b/libsolidity/formal/PredicateInstance.cpp @@ -70,14 +70,14 @@ smtutil::Expression constructor(Predicate const& _pred, EncodingContext& _contex return _pred(stateExprs + initialStateVariables(contract, _context) + currentStateVariables(contract, _context)); } -smtutil::Expression constructorCall(Predicate const& _pred, EncodingContext& _context) +smtutil::Expression constructorCall(Predicate const& _pred, EncodingContext& _context, bool _internal) { auto const& contract = dynamic_cast(*_pred.programNode()); if (auto const* constructor = contract.constructor()) - return _pred(currentFunctionVariablesForCall(*constructor, &contract, _context)); + return _pred(currentFunctionVariablesForCall(*constructor, &contract, _context, _internal)); auto& state = _context.state(); - vector stateExprs{state.errorFlag().currentValue(), state.thisAddress(0), state.abi(0), state.crypto(0), state.tx(0), state.state()}; + vector stateExprs{state.errorFlag().currentValue(), _internal ? state.thisAddress(0) : state.thisAddress(), state.abi(0), state.crypto(0), _internal ? state.tx(0) : state.tx(), state.state()}; state.newState(); stateExprs += vector{state.state()}; stateExprs += currentStateVariables(contract, _context); @@ -166,11 +166,12 @@ vector currentFunctionVariablesForDefinition( vector currentFunctionVariablesForCall( FunctionDefinition const& _function, ContractDefinition const* _contract, - EncodingContext& _context + EncodingContext& _context, + bool _internal ) { auto& state = _context.state(); - vector exprs{state.errorFlag().currentValue(), state.thisAddress(0), state.abi(0), state.crypto(0), state.tx(0), state.state()}; + vector exprs{state.errorFlag().currentValue(), _internal ? state.thisAddress(0) : state.thisAddress(), state.abi(0), state.crypto(0), _internal ? state.tx(0) : state.tx(), state.state()}; exprs += _contract ? currentStateVariables(*_contract, _context) : vector{}; exprs += applyMap(_function.parameters(), [&](auto _var) { return _context.variable(*_var)->currentValue(); }); diff --git a/libsolidity/formal/PredicateInstance.h b/libsolidity/formal/PredicateInstance.h index 2f6c77381319..42a9e2040b36 100644 --- a/libsolidity/formal/PredicateInstance.h +++ b/libsolidity/formal/PredicateInstance.h @@ -37,7 +37,14 @@ smtutil::Expression interface(Predicate const& _pred, ContractDefinition const& smtutil::Expression nondetInterface(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context, unsigned _preIdx, unsigned _postIdx); smtutil::Expression constructor(Predicate const& _pred, EncodingContext& _context); -smtutil::Expression constructorCall(Predicate const& _pred, EncodingContext& _context); +/// The encoding of the deployment procedure includes adding constraints +/// for base constructors if inheritance is used. +/// From the predicate point of view this is not different, +/// but some of the arguments are different. +/// @param _internal = true means that this constructor call is used in the +/// deployment procedure, whereas false means it is used in the deployment +/// of a contract. +smtutil::Expression constructorCall(Predicate const& _pred, EncodingContext& _context, bool _internal = true); smtutil::Expression function( Predicate const& _pred, @@ -77,7 +84,8 @@ std::vector currentFunctionVariablesForDefinition( std::vector currentFunctionVariablesForCall( FunctionDefinition const& _function, ContractDefinition const* _contract, - EncodingContext& _context + EncodingContext& _context, + bool _internal = true ); std::vector currentBlockVariables( diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 810b533512fd..c3286fa041bc 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -637,7 +637,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) visitGasLeft(_funCall); break; case FunctionType::Kind::External: - if (isPublicGetter(_funCall.expression())) + if (publicGetter(_funCall.expression())) visitPublicGetter(_funCall); break; case FunctionType::Kind::ABIDecode: @@ -696,10 +696,18 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) case FunctionType::Kind::ObjectCreation: visitObjectCreation(_funCall); return; + case FunctionType::Kind::Creation: + if (!m_settings.engine.chc || !m_settings.externalCalls.isTrusted()) + m_errorReporter.warning( + 8729_error, + _funCall.location(), + "Contract deployment is only supported in the trusted mode for external calls" + " with the CHC engine." + ); + break; case FunctionType::Kind::DelegateCall: case FunctionType::Kind::BareCallCode: case FunctionType::Kind::BareDelegateCall: - case FunctionType::Kind::Creation: default: m_errorReporter.warning( 4588_error, @@ -978,9 +986,8 @@ vector structGetterReturnedMembers(StructType const& _structType) void SMTEncoder::visitPublicGetter(FunctionCall const& _funCall) { - MemberAccess const& access = dynamic_cast(_funCall.expression()); - auto var = dynamic_cast(access.annotation().referencedDeclaration); - solAssert(var, ""); + auto var = publicGetter(_funCall.expression()); + solAssert(var && var->isStateVariable(), ""); solAssert(m_context.knownExpression(_funCall), ""); auto paramExpectedTypes = replaceUserTypes(FunctionType(*var).parameterTypes()); auto actualArguments = _funCall.arguments(); @@ -1054,7 +1061,7 @@ bool SMTEncoder::shouldAnalyze(ContractDefinition const& _contract) const return false; return m_settings.contracts.isDefault() || - m_settings.contracts.has(_contract.sourceUnitName(), _contract.name()); + m_settings.contracts.has(_contract.sourceUnitName()); } void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall) @@ -2789,16 +2796,24 @@ MemberAccess const* SMTEncoder::isEmptyPush(Expression const& _expr) const return nullptr; } -bool SMTEncoder::isPublicGetter(Expression const& _expr) { - if (!isTrustedExternalCall(&_expr)) - return false; - auto varDecl = dynamic_cast( - dynamic_cast(_expr).annotation().referencedDeclaration - ); - return varDecl != nullptr; +smtutil::Expression SMTEncoder::contractAddressValue(FunctionCall const& _f) +{ + FunctionType const& funType = dynamic_cast(*_f.expression().annotation().type); + if (funType.kind() == FunctionType::Kind::Internal) + return state().thisAddress(); + auto [funExpr, funOptions] = functionCallExpression(_f); + if (MemberAccess const* callBase = dynamic_cast(funExpr)) + return expr(callBase->expression()); + solAssert(false, "Unreachable!"); +} + +VariableDeclaration const* SMTEncoder::publicGetter(Expression const& _expr) const { + if (auto memberAccess = dynamic_cast(&_expr)) + return dynamic_cast(memberAccess->annotation().referencedDeclaration); + return nullptr; } -bool SMTEncoder::isTrustedExternalCall(Expression const* _expr) { +bool SMTEncoder::isExternalCallToThis(Expression const* _expr) { auto memberAccess = dynamic_cast(_expr); if (!memberAccess) return false; @@ -3060,7 +3075,7 @@ RationalNumberType const* SMTEncoder::isConstant(Expression const& _expr) return nullptr; } -set SMTEncoder::collectABICalls(ASTNode const* _node) +set> SMTEncoder::collectABICalls(ASTNode const* _node) { struct ABIFunctions: public ASTConstVisitor { @@ -3082,7 +3097,7 @@ set SMTEncoder::collectABICalls(ASTNode const* _node) } } - set abiCalls; + set> abiCalls; }; return ABIFunctions(_node).abiCalls; diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index 2ba491a0b053..06b03a7eb8a7 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -123,7 +123,7 @@ class SMTEncoder: public ASTConstVisitor /// RationalNumberType or can be const evaluated, and nullptr otherwise. static RationalNumberType const* isConstant(Expression const& _expr); - static std::set collectABICalls(ASTNode const* _node); + static std::set> collectABICalls(ASTNode const* _node); /// @returns all the sources that @param _source depends on, /// including itself. @@ -219,7 +219,7 @@ class SMTEncoder: public ASTConstVisitor void visitTypeConversion(FunctionCall const& _funCall); void visitStructConstructorCall(FunctionCall const& _funCall); void visitFunctionIdentifier(Identifier const& _identifier); - void visitPublicGetter(FunctionCall const& _funCall); + virtual void visitPublicGetter(FunctionCall const& _funCall); /// @returns true if @param _contract is set for analysis in the settings /// and it is not abstract. @@ -227,7 +227,12 @@ class SMTEncoder: public ASTConstVisitor /// @returns true if @param _source is set for analysis in the settings. bool shouldAnalyze(SourceUnit const& _source) const; - bool isPublicGetter(Expression const& _expr); + /// @returns the state variable returned by a public getter if + /// @a _expr is a call to a public getter, + /// otherwise nullptr. + VariableDeclaration const* publicGetter(Expression const& _expr) const; + + smtutil::Expression contractAddressValue(FunctionCall const& _f); /// Encodes a modifier or function body according to the modifier /// visit depth. @@ -392,9 +397,9 @@ class SMTEncoder: public ASTConstVisitor /// otherwise nullptr. MemberAccess const* isEmptyPush(Expression const& _expr) const; - /// @returns true if the given identifier is a contract which is known and trusted. + /// @returns true if the given expression is `this`. /// This means we don't have to abstract away effects of external function calls to this contract. - static bool isTrustedExternalCall(Expression const* _expr); + static bool isExternalCallToThis(Expression const* _expr); /// Creates symbolic expressions for the returned values /// and set them as the components of the symbolic tuple. diff --git a/libsolidity/formal/SymbolicState.cpp b/libsolidity/formal/SymbolicState.cpp index 3afde0dcf553..68bd9b64621f 100644 --- a/libsolidity/formal/SymbolicState.cpp +++ b/libsolidity/formal/SymbolicState.cpp @@ -22,8 +22,13 @@ #include #include +#include + +#include + using namespace std; using namespace solidity; +using namespace solidity::util; using namespace solidity::smtutil; using namespace solidity::frontend::smt; @@ -58,16 +63,8 @@ smtutil::Expression BlockchainVariable::member(string const& _member) const smtutil::Expression BlockchainVariable::assignMember(string const& _member, smtutil::Expression const& _value) { - vector args; - for (auto const& m: m_members) - if (m.first == _member) - args.emplace_back(_value); - else - args.emplace_back(member(m.first)); - m_tuple->increaseIndex(); - auto tuple = m_tuple->currentValue(); - auto sortExpr = smtutil::Expression(make_shared(tuple.sort), tuple.name); - m_context.addAssertion(tuple == smtutil::Expression::tuple_constructor(sortExpr, args)); + smtutil::Expression newTuple = smt::assignMember(m_tuple->currentValue(), {{_member, _value}}); + m_context.addAssertion(m_tuple->increaseIndex() == newTuple); return m_tuple->currentValue(); } @@ -75,16 +72,19 @@ void SymbolicState::reset() { m_error.resetIndex(); m_thisAddress.resetIndex(); - m_state.reset(); m_tx.reset(); m_crypto.reset(); if (m_abi) m_abi->reset(); + /// We don't reset nor clear these pointers on purpose, + /// since it only helps to keep the already generated types. + if (m_state) + m_state->reset(); } smtutil::Expression SymbolicState::balances() const { - return m_state.member("balances"); + return m_state->member("balances"); } smtutil::Expression SymbolicState::balance() const @@ -107,24 +107,94 @@ void SymbolicState::newBalances() auto tupleSort = dynamic_pointer_cast(stateSort()); auto balanceSort = tupleSort->components.at(tupleSort->memberToIndex.at("balances")); SymbolicVariable newBalances(balanceSort, "fresh_balances_" + to_string(m_context.newUniqueId()), m_context); - m_state.assignMember("balances", newBalances.currentValue()); + m_state->assignMember("balances", newBalances.currentValue()); } void SymbolicState::transfer(smtutil::Expression _from, smtutil::Expression _to, smtutil::Expression _value) { - unsigned indexBefore = m_state.index(); + unsigned indexBefore = m_state->index(); addBalance(_from, 0 - _value); addBalance(_to, std::move(_value)); - unsigned indexAfter = m_state.index(); + unsigned indexAfter = m_state->index(); solAssert(indexAfter > indexBefore, ""); - m_state.newVar(); + m_state->newVar(); /// Do not apply the transfer operation if _from == _to. auto newState = smtutil::Expression::ite( std::move(_from) == std::move(_to), - m_state.value(indexBefore), - m_state.value(indexAfter) + m_state->value(indexBefore), + m_state->value(indexAfter) + ); + m_context.addAssertion(m_state->value() == newState); +} + +smtutil::Expression SymbolicState::storage(ContractDefinition const& _contract) const +{ + return smt::member(m_state->member("storage"), contractStorageKey(_contract)); +} + +smtutil::Expression SymbolicState::storage(ContractDefinition const& _contract, smtutil::Expression _address) const +{ + return smtutil::Expression::select(storage(_contract), std::move(_address)); +} + +smtutil::Expression SymbolicState::addressActive(smtutil::Expression _address) const +{ + return smtutil::Expression::select(m_state->member("isActive"), std::move(_address)); +} + +void SymbolicState::setAddressActive( + smtutil::Expression _address, + bool _active +) +{ + m_state->assignMember("isActive", smtutil::Expression::store( + m_state->member("isActive"), + std::move(_address), + smtutil::Expression(_active)) + ); +} + +void SymbolicState::newStorage() +{ + auto newStorageVar = SymbolicTupleVariable( + m_state->member("storage").sort, + "havoc_storage_" + to_string(m_context.newUniqueId()), + m_context ); - m_context.addAssertion(m_state.value() == newState); + m_state->assignMember("storage", newStorageVar.currentValue()); +} + +void SymbolicState::writeStateVars(ContractDefinition const& _contract, smtutil::Expression _address) +{ + auto stateVars = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract); + if (stateVars.empty()) + return; + + map values; + for (auto var: stateVars) + values.emplace(stateVarStorageKey(*var, _contract), m_context.variable(*var)->currentValue()); + + smtutil::Expression thisStorage = storage(_contract, _address); + smtutil::Expression newStorage = smt::assignMember(thisStorage, values); + auto newContractStorage = smtutil::Expression::store( + storage(_contract), std::move(_address), newStorage + ); + smtutil::Expression newAllStorage = smt::assignMember(m_state->member("storage"), {{contractStorageKey(_contract), newContractStorage}}); + m_state->assignMember("storage", newAllStorage); +} + +void SymbolicState::readStateVars(ContractDefinition const& _contract, smtutil::Expression _address) +{ + auto stateVars = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract); + if (stateVars.empty()) + return; + + auto contractStorage = storage(_contract, std::move(_address)); + for (auto var: stateVars) + m_context.addAssertion( + m_context.variable(*var)->increaseIndex() == + smt::member(contractStorage, stateVarStorageKey(*var, _contract)) + ); } void SymbolicState::addBalance(smtutil::Expression _address, smtutil::Expression _value) @@ -134,7 +204,7 @@ void SymbolicState::addBalance(smtutil::Expression _address, smtutil::Expression _address, balance(_address) + std::move(_value) ); - m_state.assignMember("balances", newBalances); + m_state->assignMember("balances", newBalances); } smtutil::Expression SymbolicState::txMember(string const& _member) const @@ -194,17 +264,99 @@ smtutil::Expression SymbolicState::txFunctionConstraints(FunctionDefinition cons return conj; } -void SymbolicState::prepareForSourceUnit(SourceUnit const& _source) +void SymbolicState::prepareForSourceUnit(SourceUnit const& _source, bool _storage) { - set abiCalls = SMTEncoder::collectABICalls(&_source); - for (auto const& source: _source.referencedSourceUnits(true)) + auto allSources = _source.referencedSourceUnits(true); + allSources.insert(&_source); + set> abiCalls; + set> contracts; + for (auto const& source: allSources) + { abiCalls += SMTEncoder::collectABICalls(source); + for (auto node: source->nodes()) + if (auto contract = dynamic_cast(node.get())) + contracts.insert(contract); + } + buildState(contracts, _storage); buildABIFunctions(abiCalls); } /// Private helpers. -void SymbolicState::buildABIFunctions(set const& _abiFunctions) +string SymbolicState::contractSuffix(ContractDefinition const& _contract) const +{ + return "_" + _contract.name() + "_" + to_string(_contract.id()); +} + +string SymbolicState::contractStorageKey(ContractDefinition const& _contract) const +{ + return "storage" + contractSuffix(_contract); +} + +string SymbolicState::stateVarStorageKey(VariableDeclaration const& _var, ContractDefinition const& _contract) const +{ + return _var.name() + "_" + to_string(_var.id()) + contractSuffix(_contract); +} + +void SymbolicState::buildState(set> const& _contracts, bool _allStorages) +{ + map stateMembers{ + {"balances", make_shared(smtutil::SortProvider::uintSort, smtutil::SortProvider::uintSort)} + }; + + if (_allStorages) + { + vector memberNames; + vector memberSorts; + for (auto contract: _contracts) + { + string suffix = contractSuffix(*contract); + + // z3 doesn't like empty tuples, so if the contract has 0 + // state vars we can't put it there. + auto stateVars = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*contract); + if (stateVars.empty()) + continue; + + auto names = applyMap(stateVars, [&](auto var) { + return var->name() + "_" + to_string(var->id()) + suffix; + }); + auto sorts = applyMap(stateVars, [](auto var) { return smtSortAbstractFunction(*var->type()); }); + + string name = "storage" + suffix; + auto storageTuple = make_shared( + name + "_type", names, sorts + ); + + auto storageSort = make_shared( + smtSort(*TypeProvider::address()), + storageTuple + ); + + memberNames.emplace_back(name); + memberSorts.emplace_back(storageSort); + } + + stateMembers.emplace( + "isActive", + make_shared(smtSort(*TypeProvider::address()), smtutil::SortProvider::boolSort) + ); + stateMembers.emplace( + "storage", + make_shared( + "storage_type", memberNames, memberSorts + ) + ); + } + + m_state = make_unique( + "state", + std::move(stateMembers), + m_context + ); +} + +void SymbolicState::buildABIFunctions(set> const& _abiFunctions) { map functions; diff --git a/libsolidity/formal/SymbolicState.h b/libsolidity/formal/SymbolicState.h index 45c129c1b119..7fe5b028d627 100644 --- a/libsolidity/formal/SymbolicState.h +++ b/libsolidity/formal/SymbolicState.h @@ -62,7 +62,8 @@ class BlockchainVariable * - this (the address of the currently executing contract) * - state, represented as a tuple of: * - balances - * - TODO: potentially storage of contracts + * - array of address => bool representing whether an address is used by a contract + * - storage of contracts * - block and transaction properties, represented as a tuple of: * - blockhash * - block basefee @@ -99,29 +100,41 @@ class SymbolicState /// @returns the symbolic value of the currently executing contract's address. smtutil::Expression thisAddress() const { return m_thisAddress.currentValue(); } smtutil::Expression thisAddress(unsigned _idx) const { return m_thisAddress.valueAtIndex(_idx); } + smtutil::Expression newThisAddress() { return m_thisAddress.increaseIndex(); } smtutil::SortPointer const& thisAddressSort() const { return m_thisAddress.sort(); } //@} /// Blockchain state. //@{ - smtutil::Expression state() const { return m_state.value(); } - smtutil::Expression state(unsigned _idx) const { return m_state.value(_idx); } - smtutil::SortPointer const& stateSort() const { return m_state.sort(); } - void newState() { m_state.newVar(); } + smtutil::Expression state() const { solAssert(m_state, ""); return m_state->value(); } + smtutil::Expression state(unsigned _idx) const { solAssert(m_state, ""); return m_state->value(_idx); } + smtutil::SortPointer const& stateSort() const { solAssert(m_state, ""); return m_state->sort(); } + void newState() { solAssert(m_state, ""); m_state->newVar(); } void newBalances(); + + /// Balance. /// @returns the symbolic balances. smtutil::Expression balances() const; /// @returns the symbolic balance of address `this`. smtutil::Expression balance() const; /// @returns the symbolic balance of an address. smtutil::Expression balance(smtutil::Expression _address) const; - /// Transfer _value from _from to _to. void transfer(smtutil::Expression _from, smtutil::Expression _to, smtutil::Expression _value); /// Adds _value to _account's balance. void addBalance(smtutil::Expression _account, smtutil::Expression _value); + + /// Storage. + smtutil::Expression storage(ContractDefinition const& _contract) const; + smtutil::Expression storage(ContractDefinition const& _contract, smtutil::Expression _address) const; + smtutil::Expression addressActive(smtutil::Expression _address) const; + void setAddressActive(smtutil::Expression _address, bool _active); + + void newStorage(); + void writeStateVars(ContractDefinition const& _contract, smtutil::Expression _address); + void readStateVars(ContractDefinition const& _contract, smtutil::Expression _address); //@} /// Transaction data. @@ -149,11 +162,15 @@ class SymbolicState smtutil::Expression cryptoFunction(std::string const& _member) const { return m_crypto.member(_member); } //@} + /// Calls the internal methods that build + /// - the symbolic ABI functions based on the abi.* calls + /// in _source and referenced sources. + /// - the symbolic storages for all contracts in _source and + /// referenced sources. + void prepareForSourceUnit(SourceUnit const& _source, bool _storage); + /// ABI functions. //@{ - /// Calls the internal methods that build the symbolic ABI functions - /// based on the abi.* calls in _source and referenced sources. - void prepareForSourceUnit(SourceUnit const& _source); smtutil::Expression abiFunction(FunctionCall const* _funCall); using SymbolicABIFunction = std::tuple< std::string, @@ -169,8 +186,15 @@ class SymbolicState //@} private: + std::string contractSuffix(ContractDefinition const& _contract) const; + std::string contractStorageKey(ContractDefinition const& _contract) const; + std::string stateVarStorageKey(VariableDeclaration const& _var, ContractDefinition const& _contract) const; + + /// Builds state.storage based on _contracts. + void buildState(std::set> const& _contracts, bool _allStorages); + /// Builds m_abi based on the abi.* calls _abiFunctions. - void buildABIFunctions(std::set const& _abiFunctions); + void buildABIFunctions(std::set> const& _abiFunctions); EncodingContext& m_context; @@ -186,11 +210,14 @@ class SymbolicState m_context }; - BlockchainVariable m_state{ - "state", - {{"balances", std::make_shared(smtutil::SortProvider::uintSort, smtutil::SortProvider::uintSort)}}, - m_context - }; + /// m_state is a tuple of + /// - balances: array of address to balance of address. + /// - isActive: array of address to Boolean, where element is true iff address is used. + /// - storage: tuple containing the storage of every contract, where + /// each element of the tuple represents a contract, + /// and is defined by an array where the index is the contract's address + /// and the element is a tuple containing the state variables of that contract. + std::unique_ptr m_state; BlockchainVariable m_tx{ "tx", diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index d40b51cea4fb..ccabebcc3bfc 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -618,4 +618,26 @@ optional symbolicTypeConversion(frontend::Type const* _from return std::nullopt; } +smtutil::Expression member(smtutil::Expression const& _tuple, string const& _member) +{ + TupleSort const& _sort = dynamic_cast(*_tuple.sort); + return smtutil::Expression::tuple_get( + _tuple, + _sort.memberToIndex.at(_member) + ); +} + +smtutil::Expression assignMember(smtutil::Expression const _tuple, map const& _values) +{ + TupleSort const& _sort = dynamic_cast(*_tuple.sort); + vector args; + for (auto const& m: _sort.members) + if (auto* value = util::valueOrNullptr(_values, m)) + args.emplace_back(*value); + else + args.emplace_back(member(_tuple, m)); + auto sortExpr = smtutil::Expression(make_shared(_tuple.sort), _tuple.name); + return smtutil::Expression::tuple_constructor(sortExpr, args); +} + } diff --git a/libsolidity/formal/SymbolicTypes.h b/libsolidity/formal/SymbolicTypes.h index 312e8581a963..747e27b7e372 100644 --- a/libsolidity/formal/SymbolicTypes.h +++ b/libsolidity/formal/SymbolicTypes.h @@ -82,4 +82,8 @@ void setSymbolicUnknownValue(smtutil::Expression _expr, frontend::Type const* _t smtutil::Expression symbolicUnknownConstraints(smtutil::Expression _expr, frontend::Type const* _type); std::optional symbolicTypeConversion(frontend::Type const* _from, frontend::Type const* _to); + +smtutil::Expression member(smtutil::Expression const& _tuple, std::string const& _member); +smtutil::Expression assignMember(smtutil::Expression const _tuple, std::map const& _values); + } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 27fa1c98964e..b08e8fbf6fa3 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -445,7 +445,7 @@ std::optional checkSettingsKeys(Json::Value const& _input) std::optional checkModelCheckerSettingsKeys(Json::Value const& _input) { - static set keys{"contracts", "divModNoSlacks", "engine", "invariants", "showUnproved", "solvers", "targets", "timeout"}; + static set keys{"contracts", "divModNoSlacks", "engine", "extCalls", "invariants", "showUnproved", "solvers", "targets", "timeout"}; return checkKeys(_input, keys, "modelChecker"); } @@ -1016,6 +1016,16 @@ std::variant StandardCompiler: ret.modelCheckerSettings.engine = *engine; } + if (modelCheckerSettings.isMember("extCalls")) + { + if (!modelCheckerSettings["extCalls"].isString()) + return formatFatalError(Error::Type::JSONError, "settings.modelChecker.extCalls must be a string."); + std::optional extCalls = ModelCheckerExtCalls::fromString(modelCheckerSettings["extCalls"].asString()); + if (!extCalls) + return formatFatalError(Error::Type::JSONError, "Invalid model checker extCalls requested."); + ret.modelCheckerSettings.externalCalls = *extCalls; + } + if (modelCheckerSettings.isMember("invariants")) { auto const& invariantsArray = modelCheckerSettings["invariants"]; diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 37ceca1d4007..eb03831beb33 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -69,6 +69,7 @@ static string const g_strMetadataLiteral = "metadata-literal"; static string const g_strModelCheckerContracts = "model-checker-contracts"; static string const g_strModelCheckerDivModNoSlacks = "model-checker-div-mod-no-slacks"; static string const g_strModelCheckerEngine = "model-checker-engine"; +static string const g_strModelCheckerExtCalls = "model-checker-ext-calls"; static string const g_strModelCheckerInvariants = "model-checker-invariants"; static string const g_strModelCheckerShowUnproved = "model-checker-show-unproved"; static string const g_strModelCheckerSolvers = "model-checker-solvers"; @@ -831,6 +832,12 @@ General Information)").c_str(), po::value()->value_name("all,bmc,chc,none")->default_value("none"), "Select model checker engine." ) + ( + g_strModelCheckerExtCalls.c_str(), + po::value()->value_name("untrusted,trusted")->default_value("untrusted"), + "Select whether to assume (trusted) that external calls always invoke" + " the code given by the type of the contract, if that code is available." + ) ( g_strModelCheckerInvariants.c_str(), po::value()->value_name("default,all,contract,reentrancy")->default_value("default"), @@ -1289,6 +1296,15 @@ void CommandLineParser::processArgs() m_options.modelChecker.settings.engine = *engine; } + if (m_args.count(g_strModelCheckerExtCalls)) + { + string mode = m_args[g_strModelCheckerExtCalls].as(); + optional extCallsMode = ModelCheckerExtCalls::fromString(mode); + if (!extCallsMode) + solThrow(CommandLineValidationError, "Invalid option for --" + g_strModelCheckerExtCalls + ": " + mode); + m_options.modelChecker.settings.externalCalls = *extCallsMode; + } + if (m_args.count(g_strModelCheckerInvariants)) { string invsStr = m_args[g_strModelCheckerInvariants].as(); @@ -1327,6 +1343,7 @@ void CommandLineParser::processArgs() m_args.count(g_strModelCheckerContracts) || m_args.count(g_strModelCheckerDivModNoSlacks) || m_args.count(g_strModelCheckerEngine) || + m_args.count(g_strModelCheckerExtCalls) || m_args.count(g_strModelCheckerInvariants) || m_args.count(g_strModelCheckerShowUnproved) || m_args.count(g_strModelCheckerSolvers) || diff --git a/test/cmdlineTests/model_checker_contracts_inexistent_contract/err b/test/cmdlineTests/model_checker_contracts_inexistent_contract/err index 6326537e928c..00b97763f340 100644 --- a/test/cmdlineTests/model_checker_contracts_inexistent_contract/err +++ b/test/cmdlineTests/model_checker_contracts_inexistent_contract/err @@ -1 +1,27 @@ Warning: Requested contract "C" does not exist in source "model_checker_contracts_inexistent_contract/input.sol". + +Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +B.constructor() +B.f(0) + --> model_checker_contracts_inexistent_contract/input.sol:5:3: + | +5 | assert(x > 0); + | ^^^^^^^^^^^^^ + +Warning: CHC: Assertion violation happens here. +Counterexample: + +y = 0 + +Transaction trace: +A.constructor() +A.g(0) + --> model_checker_contracts_inexistent_contract/input.sol:10:3: + | +10 | assert(y > 0); + | ^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_contracts_only_one/err b/test/cmdlineTests/model_checker_contracts_only_one/err index 9c8ade6931c3..2088e19c7a2f 100644 --- a/test/cmdlineTests/model_checker_contracts_only_one/err +++ b/test/cmdlineTests/model_checker_contracts_only_one/err @@ -4,7 +4,7 @@ Counterexample: x = 0 Transaction trace: -A.constructor() +B.constructor() B.f(0) --> model_checker_contracts_only_one/input.sol:5:3: | diff --git a/test/cmdlineTests/model_checker_ext_calls_empty_arg/args b/test/cmdlineTests/model_checker_ext_calls_empty_arg/args new file mode 100644 index 000000000000..b0c065ef7b5a --- /dev/null +++ b/test/cmdlineTests/model_checker_ext_calls_empty_arg/args @@ -0,0 +1 @@ +--model-checker-engine chc --model-checker-ext-calls diff --git a/test/cmdlineTests/model_checker_ext_calls_empty_arg/err b/test/cmdlineTests/model_checker_ext_calls_empty_arg/err new file mode 100644 index 000000000000..7d7481c80ebe --- /dev/null +++ b/test/cmdlineTests/model_checker_ext_calls_empty_arg/err @@ -0,0 +1 @@ +No input files given. If you wish to use the standard input please specify "-" explicitly. diff --git a/test/cmdlineTests/model_checker_ext_calls_empty_arg/exit b/test/cmdlineTests/model_checker_ext_calls_empty_arg/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/model_checker_ext_calls_empty_arg/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/model_checker_ext_calls_empty_arg/input.sol b/test/cmdlineTests/model_checker_ext_calls_empty_arg/input.sol new file mode 100644 index 000000000000..bd341cea6058 --- /dev/null +++ b/test/cmdlineTests/model_checker_ext_calls_empty_arg/input.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract Ext { + function f() public view returns (uint) { + return 42; + } +} + +contract test { + function g(Ext e) public view { + uint x = e.f(); + assert(x == 42); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_ext_calls_trusted_chc/args b/test/cmdlineTests/model_checker_ext_calls_trusted_chc/args new file mode 100644 index 000000000000..a6240ee19fbb --- /dev/null +++ b/test/cmdlineTests/model_checker_ext_calls_trusted_chc/args @@ -0,0 +1 @@ +--model-checker-engine chc --model-checker-ext-calls trusted diff --git a/test/cmdlineTests/model_checker_ext_calls_trusted_chc/err b/test/cmdlineTests/model_checker_ext_calls_trusted_chc/err new file mode 100644 index 000000000000..a07e1575d145 --- /dev/null +++ b/test/cmdlineTests/model_checker_ext_calls_trusted_chc/err @@ -0,0 +1,5 @@ +Warning: Function state mutability can be restricted to pure + --> model_checker_ext_calls_trusted_chc/input.sol:5:2: + | +5 | function f() public view returns (uint) { + | ^ (Relevant source part starts here and spans across multiple lines). diff --git a/test/cmdlineTests/model_checker_ext_calls_trusted_chc/input.sol b/test/cmdlineTests/model_checker_ext_calls_trusted_chc/input.sol new file mode 100644 index 000000000000..bd341cea6058 --- /dev/null +++ b/test/cmdlineTests/model_checker_ext_calls_trusted_chc/input.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract Ext { + function f() public view returns (uint) { + return 42; + } +} + +contract test { + function g(Ext e) public view { + uint x = e.f(); + assert(x == 42); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_ext_calls_untrusted_chc/args b/test/cmdlineTests/model_checker_ext_calls_untrusted_chc/args new file mode 100644 index 000000000000..e249f0250329 --- /dev/null +++ b/test/cmdlineTests/model_checker_ext_calls_untrusted_chc/args @@ -0,0 +1 @@ +--model-checker-engine chc --model-checker-ext-calls untrusted diff --git a/test/cmdlineTests/model_checker_ext_calls_untrusted_chc/err b/test/cmdlineTests/model_checker_ext_calls_untrusted_chc/err new file mode 100644 index 000000000000..b06333e43bd5 --- /dev/null +++ b/test/cmdlineTests/model_checker_ext_calls_untrusted_chc/err @@ -0,0 +1,14 @@ +Warning: CHC: Assertion violation happens here. +Counterexample: + +e = 0 +x = 1 + +Transaction trace: +test.constructor() +test.g(0) + e.f() -- untrusted external call + --> model_checker_ext_calls_untrusted_chc/input.sol:11:3: + | +11 | assert(x == 0); + | ^^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_ext_calls_untrusted_chc/input.sol b/test/cmdlineTests/model_checker_ext_calls_untrusted_chc/input.sol new file mode 100644 index 000000000000..0d28d1c18bb8 --- /dev/null +++ b/test/cmdlineTests/model_checker_ext_calls_untrusted_chc/input.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +abstract contract Ext { + function f() virtual public view returns (uint); +} + +contract test { + function g(Ext e) public view { + uint x = e.f(); + assert(x == 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_ext_calls_wrong_arg/args b/test/cmdlineTests/model_checker_ext_calls_wrong_arg/args new file mode 100644 index 000000000000..478aa5e1d3a9 --- /dev/null +++ b/test/cmdlineTests/model_checker_ext_calls_wrong_arg/args @@ -0,0 +1 @@ +--model-checker-engine chc --model-checker-ext-calls what diff --git a/test/cmdlineTests/model_checker_ext_calls_wrong_arg/err b/test/cmdlineTests/model_checker_ext_calls_wrong_arg/err new file mode 100644 index 000000000000..746a0b525229 --- /dev/null +++ b/test/cmdlineTests/model_checker_ext_calls_wrong_arg/err @@ -0,0 +1 @@ +Invalid option for --model-checker-ext-calls: what diff --git a/test/cmdlineTests/model_checker_ext_calls_wrong_arg/exit b/test/cmdlineTests/model_checker_ext_calls_wrong_arg/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/model_checker_ext_calls_wrong_arg/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/model_checker_ext_calls_wrong_arg/input.sol b/test/cmdlineTests/model_checker_ext_calls_wrong_arg/input.sol new file mode 100644 index 000000000000..bd341cea6058 --- /dev/null +++ b/test/cmdlineTests/model_checker_ext_calls_wrong_arg/input.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract Ext { + function f() public view returns (uint) { + return 42; + } +} + +contract test { + function g(Ext e) public view { + uint x = e.f(); + assert(x == 42); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/standard_model_checker_contracts_inexistent_contract/output.json b/test/cmdlineTests/standard_model_checker_contracts_inexistent_contract/output.json index e3714e8e685f..7ff30950c1ba 100644 --- a/test/cmdlineTests/standard_model_checker_contracts_inexistent_contract/output.json +++ b/test/cmdlineTests/standard_model_checker_contracts_inexistent_contract/output.json @@ -10,6 +10,74 @@ "message": "Requested contract \"C\" does not exist in source \"Source\".", "severity": "warning", "type": "Warning" + }, + { + "component": "general", + "errorCode": "6328", + "formattedMessage": "Warning: CHC: Assertion violation happens here. +Counterexample: + +y = 0 + +Transaction trace: +B.constructor() +B.g(0) + --> Source:5:7: + | +5 | \t\t\t\t\t\tassert(y > 0); + | \t\t\t\t\t\t^^^^^^^^^^^^^ + +", + "message": "CHC: Assertion violation happens here. +Counterexample: + +y = 0 + +Transaction trace: +B.constructor() +B.g(0)", + "severity": "warning", + "sourceLocation": + { + "end": 137, + "file": "Source", + "start": 124 + }, + "type": "Warning" + }, + { + "component": "general", + "errorCode": "6328", + "formattedMessage": "Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +A.constructor() +A.f(0) + --> Source:10:7: + | +10 | \t\t\t\t\t\tassert(x > 0); + | \t\t\t\t\t\t^^^^^^^^^^^^^ + +", + "message": "CHC: Assertion violation happens here. +Counterexample: + +x = 0 + +Transaction trace: +A.constructor() +A.f(0)", + "severity": "warning", + "sourceLocation": + { + "end": 231, + "file": "Source", + "start": 218 + }, + "type": "Warning" } ], "sources": diff --git a/test/cmdlineTests/standard_model_checker_contracts_multi_source/output.json b/test/cmdlineTests/standard_model_checker_contracts_multi_source/output.json index a875665817d8..cb18875e1bf4 100644 --- a/test/cmdlineTests/standard_model_checker_contracts_multi_source/output.json +++ b/test/cmdlineTests/standard_model_checker_contracts_multi_source/output.json @@ -10,7 +10,7 @@ Counterexample: y = 0 Transaction trace: -A.constructor() +B.constructor() B.g(0) --> Source:5:7: | @@ -24,7 +24,7 @@ Counterexample: y = 0 Transaction trace: -A.constructor() +B.constructor() B.g(0)", "severity": "warning", "sourceLocation": diff --git a/test/cmdlineTests/standard_model_checker_contracts_only_one/output.json b/test/cmdlineTests/standard_model_checker_contracts_only_one/output.json index 57c4f10fac8f..b4bfd66b8489 100644 --- a/test/cmdlineTests/standard_model_checker_contracts_only_one/output.json +++ b/test/cmdlineTests/standard_model_checker_contracts_only_one/output.json @@ -10,7 +10,7 @@ Counterexample: y = 0 Transaction trace: -A.constructor() +B.constructor() B.g(0) --> Source:5:7: | @@ -24,7 +24,7 @@ Counterexample: y = 0 Transaction trace: -A.constructor() +B.constructor() B.g(0)", "severity": "warning", "sourceLocation": diff --git a/test/cmdlineTests/standard_model_checker_ext_calls_empty_arg/input.json b/test/cmdlineTests/standard_model_checker_ext_calls_empty_arg/input.json new file mode 100644 index 000000000000..073516ddbda2 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_ext_calls_empty_arg/input.json @@ -0,0 +1,30 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; + contract Ext { + function f() public view returns (uint) { + return 42; + } + } + + contract test { + function g(Ext e) public view { + uint x = e.f(); + assert(x == 42); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "chc", + "extCalls": "" + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_ext_calls_empty_arg/output.json b/test/cmdlineTests/standard_model_checker_ext_calls_empty_arg/output.json new file mode 100644 index 000000000000..4e214a37b2db --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_ext_calls_empty_arg/output.json @@ -0,0 +1,12 @@ +{ + "errors": + [ + { + "component": "general", + "formattedMessage": "Invalid model checker extCalls requested.", + "message": "Invalid model checker extCalls requested.", + "severity": "error", + "type": "JSONError" + } + ] +} diff --git a/test/cmdlineTests/standard_model_checker_ext_calls_trusted_chc/input.json b/test/cmdlineTests/standard_model_checker_ext_calls_trusted_chc/input.json new file mode 100644 index 000000000000..a03b77f2bbba --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_ext_calls_trusted_chc/input.json @@ -0,0 +1,30 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; + contract Ext { + function f() public view returns (uint) { + return 42; + } + } + + contract test { + function g(Ext e) public view { + uint x = e.f(); + assert(x == 42); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "chc", + "extCalls": "trusted" + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_ext_calls_trusted_chc/output.json b/test/cmdlineTests/standard_model_checker_ext_calls_trusted_chc/output.json new file mode 100644 index 000000000000..93d73d1c4636 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_ext_calls_trusted_chc/output.json @@ -0,0 +1,32 @@ +{ + "errors": + [ + { + "component": "general", + "errorCode": "2018", + "formattedMessage": "Warning: Function state mutability can be restricted to pure + --> A:4:7: + | +4 | \t\t\t\t\t\tfunction f() public view returns (uint) { + | \t\t\t\t\t\t^ (Relevant source part starts here and spans across multiple lines). + +", + "message": "Function state mutability can be restricted to pure", + "severity": "warning", + "sourceLocation": + { + "end": 152, + "file": "A", + "start": 85 + }, + "type": "Warning" + } + ], + "sources": + { + "A": + { + "id": 0 + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_ext_calls_untrusted_chc/input.json b/test/cmdlineTests/standard_model_checker_ext_calls_untrusted_chc/input.json new file mode 100644 index 000000000000..9fadd08fabff --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_ext_calls_untrusted_chc/input.json @@ -0,0 +1,28 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; + abstract contract Ext { + function f() virtual public view returns (uint); + } + + contract test { + function g(Ext e) public view { + uint x = e.f(); + assert(x == 0); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "chc", + "extCalls": "untrusted" + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_ext_calls_untrusted_chc/output.json b/test/cmdlineTests/standard_model_checker_ext_calls_untrusted_chc/output.json new file mode 100644 index 000000000000..c6b072a85863 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_ext_calls_untrusted_chc/output.json @@ -0,0 +1,50 @@ +{ + "errors": + [ + { + "component": "general", + "errorCode": "6328", + "formattedMessage": "Warning: CHC: Assertion violation happens here. +Counterexample: + +e = 0 +x = 1 + +Transaction trace: +test.constructor() +test.g(0) + e.f() -- untrusted external call + --> A:10:8: + | +10 | \t\t\t\t\t\t\tassert(x == 0); + | \t\t\t\t\t\t\t^^^^^^^^^^^^^^ + +", + "message": "CHC: Assertion violation happens here. +Counterexample: + +e = 0 +x = 1 + +Transaction trace: +test.constructor() +test.g(0) + e.f() -- untrusted external call", + "severity": "warning", + "sourceLocation": + { + "end": 254, + "file": "A", + "start": 240 + }, + "type": "Warning" + } + ], + "sources": + { + "A": + { + "id": 0 + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_ext_calls_wrong_arg_1/input.json b/test/cmdlineTests/standard_model_checker_ext_calls_wrong_arg_1/input.json new file mode 100644 index 000000000000..15113c5ad879 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_ext_calls_wrong_arg_1/input.json @@ -0,0 +1,30 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; + contract Ext { + function f() public view returns (uint) { + return 42; + } + } + + contract test { + function g(Ext e) public view { + uint x = e.f(); + assert(x == 42); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "chc", + "extCalls": "what" + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_ext_calls_wrong_arg_1/output.json b/test/cmdlineTests/standard_model_checker_ext_calls_wrong_arg_1/output.json new file mode 100644 index 000000000000..4e214a37b2db --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_ext_calls_wrong_arg_1/output.json @@ -0,0 +1,12 @@ +{ + "errors": + [ + { + "component": "general", + "formattedMessage": "Invalid model checker extCalls requested.", + "message": "Invalid model checker extCalls requested.", + "severity": "error", + "type": "JSONError" + } + ] +} diff --git a/test/cmdlineTests/standard_model_checker_ext_calls_wrong_arg_2/input.json b/test/cmdlineTests/standard_model_checker_ext_calls_wrong_arg_2/input.json new file mode 100644 index 000000000000..cda245e7ff25 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_ext_calls_wrong_arg_2/input.json @@ -0,0 +1,30 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; + contract Ext { + function f() public view returns (uint) { + return 42; + } + } + + contract test { + function g(Ext e) public view { + uint x = e.f(); + assert(x == 42); + } + }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "chc", + "extCalls": 2 + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_ext_calls_wrong_arg_2/output.json b/test/cmdlineTests/standard_model_checker_ext_calls_wrong_arg_2/output.json new file mode 100644 index 000000000000..e869637ed125 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_ext_calls_wrong_arg_2/output.json @@ -0,0 +1,12 @@ +{ + "errors": + [ + { + "component": "general", + "formattedMessage": "settings.modelChecker.extCalls must be a string.", + "message": "settings.modelChecker.extCalls must be a string.", + "severity": "error", + "type": "JSONError" + } + ] +} diff --git a/test/cmdlineTests/standard_model_checker_solvers_smtlib2/output.json b/test/cmdlineTests/standard_model_checker_solvers_smtlib2/output.json index fa8726d55ebb..85a221d4cd98 100644 --- a/test/cmdlineTests/standard_model_checker_solvers_smtlib2/output.json +++ b/test/cmdlineTests/standard_model_checker_solvers_smtlib2/output.json @@ -3,14 +3,41 @@ { "smtlib2queries": { - "0x0ebc730de380833af1e52ed063befb32994bc637929c942b7fd089b7cd3ba64e": "(set-logic HORN) - + "0x75b95497d56c30e254a59358d72ddd4e78f9e90db621cfe677e85d05b2252411": "(set-option :produce-models true) +(set-logic ALL) +(declare-fun |x_3_3| () Int) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.basefee| Int) (|block.chainid| Int) (|block.coinbase| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.prevrandao| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-fun |abi_0| () |abi_type|) (declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-fun |x_3_4| () Int) +(declare-fun |x_3_0| () Int) +(declare-fun |expr_7_0| () Int) +(declare-fun |expr_8_0| () Int) +(declare-fun |expr_9_1| () Bool) + +(assert (and (and (and true true) (and (= expr_9_1 (> expr_7_0 expr_8_0)) (and (=> (and true true) true) (and (= expr_8_0 0) (and (=> (and true true) (and (>= expr_7_0 0) (<= expr_7_0 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_7_0 x_3_0) (and (and (>= x_3_0 0) (<= x_3_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (and (and (and (and (and (and (and (and (and (and (and (> (|block.prevrandao| tx_0) 18446744073709551616) (and (>= (|block.basefee| tx_0) 0) (<= (|block.basefee| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.prevrandao| tx_0) 0) (<= (|block.prevrandao| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 3017696395)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 179)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 222)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 100)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 139)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true)))))))) (not expr_9_1))) +(declare-const |EVALEXPR_0| Int) +(assert (= |EVALEXPR_0| x_3_0)) +(check-sat) +(get-value (|EVALEXPR_0| )) +", + "0xfb06d3f02a20bd362abded9ab80638bdc9dd43ccbf644517f3006206c0c47f67": "(set-logic HORN) + (declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) (declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.basefee| Int) (|block.chainid| Int) (|block.coinbase| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.prevrandao| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) (declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) (declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) (declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) (declare-fun |interface_0_C_14_0| (Int |abi_type| |crypto_type| |state_type| ) Bool) (declare-fun |nondet_interface_1_C_14_0| (Int Int |abi_type| |crypto_type| |state_type| |state_type| ) Bool) (declare-fun |summary_constructor_2_C_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| |state_type| ) Bool) @@ -97,7 +124,7 @@ (declare-fun |implicit_constructor_entry_13_C_14_0| (Int Int |abi_type| |crypto_type| |tx_type| |state_type| |state_type| ) Bool) (assert (forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (funds_2_0 Int) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (state_3 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int) (x_3_2 Int)) -(=> (and (and (and (and (= state_1 state_0) (= error_0 0)) true) true) (>= (select (|balances| state_1) this_0) (|msg.value| tx_0))) (implicit_constructor_entry_13_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1)))) +(=> (and (and (and (and (and (= state_1 state_0) (= error_0 0)) true) true) true) (>= (select (|balances| state_1) this_0) (|msg.value| tx_0))) (implicit_constructor_entry_13_C_14_0 error_0 this_0 abi_0 crypto_0 tx_0 state_0 state_1)))) (assert @@ -124,34 +151,7 @@ (assert (forall ( (abi_0 |abi_type|) (crypto_0 |crypto_type|) (error_0 Int) (error_1 Int) (expr_7_0 Int) (expr_8_0 Int) (expr_9_1 Bool) (funds_2_0 Int) (state_0 |state_type|) (state_1 |state_type|) (state_2 |state_type|) (state_3 |state_type|) (this_0 Int) (tx_0 |tx_type|) (x_3_0 Int) (x_3_1 Int) (x_3_2 Int)) (=> error_target_3_0 false))) -(check-sat)", - "0xcb822e6220a39244d26887a0fa6f62b06718359056555679fb06dd7dff18bb86": "(set-option :produce-models true) -(set-logic ALL) -(declare-fun |x_3_3| () Int) -(declare-fun |error_0| () Int) -(declare-fun |this_0| () Int) -(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) -(declare-fun |state_0| () |state_type|) -(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) -(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.basefee| Int) (|block.chainid| Int) (|block.coinbase| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.prevrandao| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) -(declare-fun |tx_0| () |tx_type|) -(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) -(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) -(declare-fun |crypto_0| () |crypto_type|) -(declare-datatypes ((|abi_type| 0)) (((|abi_type|)))) -(declare-fun |abi_0| () |abi_type|) -(declare-fun |x_3_4| () Int) -(declare-fun |x_3_0| () Int) -(declare-fun |expr_7_0| () Int) -(declare-fun |expr_8_0| () Int) -(declare-fun |expr_9_1| () Bool) - -(assert (and (and (and true true) (and (= expr_9_1 (> expr_7_0 expr_8_0)) (and (=> (and true true) true) (and (= expr_8_0 0) (and (=> (and true true) (and (>= expr_7_0 0) (<= expr_7_0 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (= expr_7_0 x_3_0) (and (and (>= x_3_0 0) (<= x_3_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (and (and (and (and (and (and (and (and (and (and (and (> (|block.prevrandao| tx_0) 18446744073709551616) (and (>= (|block.basefee| tx_0) 0) (<= (|block.basefee| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.chainid| tx_0) 0) (<= (|block.chainid| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.coinbase| tx_0) 0) (<= (|block.coinbase| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|block.prevrandao| tx_0) 0) (<= (|block.prevrandao| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.gaslimit| tx_0) 0) (<= (|block.gaslimit| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.number| tx_0) 0) (<= (|block.number| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|block.timestamp| tx_0) 0) (<= (|block.timestamp| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|msg.sender| tx_0) 0) (<= (|msg.sender| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|msg.value| tx_0) 0) (<= (|msg.value| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (>= (|tx.origin| tx_0) 0) (<= (|tx.origin| tx_0) 1461501637330902918203684832716283019655932542975))) (and (>= (|tx.gasprice| tx_0) 0) (<= (|tx.gasprice| tx_0) 115792089237316195423570985008687907853269984665640564039457584007913129639935))) (and (and (and (and (and (and (= (|msg.value| tx_0) 0) (= (|msg.sig| tx_0) 3017696395)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 0) 179)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 1) 222)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 2) 100)) (= (select (|bytes_tuple_accessor_array| (|msg.data| tx_0)) 3) 139)) (>= (|bytes_tuple_accessor_length| (|msg.data| tx_0)) 4))) true)))))))) (not expr_9_1))) -(declare-const |EVALEXPR_0| Int) -(assert (= |EVALEXPR_0| x_3_0)) -(check-sat) -(get-value (|EVALEXPR_0| )) -" +(check-sat)" } }, "errors": diff --git a/test/libsolidity/SMTCheckerTest.cpp b/test/libsolidity/SMTCheckerTest.cpp index e3e52491dd93..93590994a08c 100644 --- a/test/libsolidity/SMTCheckerTest.cpp +++ b/test/libsolidity/SMTCheckerTest.cpp @@ -33,6 +33,12 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, E if (!contract.empty()) m_modelCheckerSettings.contracts.contracts[""] = {contract}; + auto extCallsMode = ModelCheckerExtCalls::fromString(m_reader.stringSetting("SMTExtCalls", "untrusted")); + if (extCallsMode) + m_modelCheckerSettings.externalCalls = *extCallsMode; + else + BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT external calls mode.")); + auto const& showUnproved = m_reader.stringSetting("SMTShowUnproved", "yes"); if (showUnproved == "no") m_modelCheckerSettings.showUnproved = false; @@ -51,8 +57,13 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, E m_modelCheckerSettings.solvers &= ModelChecker::availableSolvers(); /// Underflow and Overflow are not enabled by default for Solidity >=0.8.7, - /// so we explicitly enable all targets for the tests. - m_modelCheckerSettings.targets = ModelCheckerTargets::All(); + /// so we explicitly enable all targets for the tests, + /// if the targets were not explicitly set by the test. + auto targets = ModelCheckerTargets::fromString(m_reader.stringSetting("SMTTargets", "all")); + if (targets) + m_modelCheckerSettings.targets = *targets; + else + BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT targets.")); auto engine = ModelCheckerEngine::fromString(m_reader.stringSetting("SMTEngine", "all")); if (engine) diff --git a/test/libsolidity/smtCheckerTests/array_members/length_1d_struct_array_2d_1.sol b/test/libsolidity/smtCheckerTests/array_members/length_1d_struct_array_2d_1.sol index ddda215df8d0..7950c861258b 100644 --- a/test/libsolidity/smtCheckerTests/array_members/length_1d_struct_array_2d_1.sol +++ b/test/libsolidity/smtCheckerTests/array_members/length_1d_struct_array_2d_1.sol @@ -22,4 +22,4 @@ contract C { // SMTEngine: all // SMTIgnoreOS: macos // ---- -// Info 1180: Contract invariant(s) for :C:\n!(s1.arr.length <= 0)\n!(s2.arr.length <= 0)\n(((s2.arr[0].length + ((- 1) * s1.arr[0].length)) <= 0) && ((s1.arr[0].length + ((- 1) * s2.arr[0].length)) <= 0))\n +// Info 1180: Contract invariant(s) for :C:\n!(s1.arr.length <= 0)\n!(s2.arr.length <= 0)\n(((s1.arr[0].length + ((- 1) * s2.arr[0].length)) <= 0) && ((s2.arr[0].length + ((- 1) * s1.arr[0].length)) <= 0))\n diff --git a/test/libsolidity/smtCheckerTests/array_members/length_same_after_assignment_3_fail.sol b/test/libsolidity/smtCheckerTests/array_members/length_same_after_assignment_3_fail.sol index 7a7db383b267..7af3af40d940 100644 --- a/test/libsolidity/smtCheckerTests/array_members/length_same_after_assignment_3_fail.sol +++ b/test/libsolidity/smtCheckerTests/array_members/length_same_after_assignment_3_fail.sol @@ -31,4 +31,4 @@ contract C { // Warning 6328: (349-375): CHC: Assertion violation happens here.\nCounterexample:\narr = [[], [], [], [], [], [], [], [], []]\nx = 0\ny = 0\nz = 9\nt = 0\n\nTransaction trace:\nC.constructor()\nState: arr = [[], [], [], [], [], [], [], [], []]\nC.f() // Warning 6328: (379-402): CHC: Assertion violation happens here.\nCounterexample:\narr = [[], [], [], [], [], [], [], [], []]\nx = 0\ny = 0\nz = 9\nt = 0\n\nTransaction trace:\nC.constructor()\nState: arr = [[], [], [], [], [], [], [], [], []]\nC.f() // Warning 6328: (406-432): CHC: Assertion violation happens here.\nCounterexample:\narr = [[], [], [], [], [], [], [], [], []]\nx = 0\ny = 0\nz = 9\nt = 0\n\nTransaction trace:\nC.constructor()\nState: arr = [[], [], [], [], [], [], [], [], []]\nC.f() -// Info 1180: Contract invariant(s) for :C:\n!(arr.length <= 7)\n!(arr.length <= 8)\n +// Info 1180: Contract invariant(s) for :C:\n!(arr.length <= 5)\n!(arr.length <= 7)\n!(arr.length <= 8)\n diff --git a/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_1d.sol b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_1d.sol index 0d16306b72bf..aea10a376971 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_1d.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_1d.sol @@ -18,5 +18,6 @@ contract C { // ==== // SMTEngine: all // SMTIgnoreOS: macos +// SMTIgnoreCex: yes // ---- // Warning 6328: (199-229): CHC: Assertion violation happens here.\nCounterexample:\nb = [1]\n\nTransaction trace:\nC.constructor()\nState: b = []\nC.g() diff --git a/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_2d.sol b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_2d.sol index bbd5589a48ba..34b439e3a616 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_2d.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_2d.sol @@ -20,5 +20,6 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- // Warning 6328: (362-420): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/complex/slither/external_function.sol b/test/libsolidity/smtCheckerTests/complex/slither/external_function.sol index 3f5096a3b56c..936ad1703784 100644 --- a/test/libsolidity/smtCheckerTests/complex/slither/external_function.sol +++ b/test/libsolidity/smtCheckerTests/complex/slither/external_function.sol @@ -83,6 +83,6 @@ contract InternalCall { // Warning 2018: (1111-1173): Function state mutability can be restricted to pure // Warning 2018: (1179-1241): Function state mutability can be restricted to pure // Warning 2018: (1247-1309): Function state mutability can be restricted to pure -// Warning 4588: (681-716): Assertion checker does not yet implement this type of function call. -// Warning 4588: (854-886): Assertion checker does not yet implement this type of function call. +// Warning 8729: (681-716): Contract deployment is only supported in the trusted mode for external calls with the CHC engine. +// Warning 8729: (854-886): Contract deployment is only supported in the trusted mode for external calls with the CHC engine. // Warning 5729: (1370-1375): BMC does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/deployment/deploy_bmc_trusted.sol b/test/libsolidity/smtCheckerTests/deployment/deploy_bmc_trusted.sol new file mode 100644 index 000000000000..81ddc515e1b4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/deployment/deploy_bmc_trusted.sol @@ -0,0 +1,18 @@ +contract D { + uint x; + function f() public view returns (uint) { return x; } +} + +contract C { + function g() public { + D d = new D(); + uint y = d.f(); + assert(y == 0); // should fail in BMC + } +} +// ==== +// SMTEngine: bmc +// SMTExtCalls: trusted +// ---- +// Warning 8729: (124-131): Contract deployment is only supported in the trusted mode for external calls with the CHC engine. +// Warning 4661: (153-167): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/deployment/deploy_bmc_untrusted.sol b/test/libsolidity/smtCheckerTests/deployment/deploy_bmc_untrusted.sol new file mode 100644 index 000000000000..f69a8d6c72eb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/deployment/deploy_bmc_untrusted.sol @@ -0,0 +1,17 @@ +contract D { + uint x; + function f() public view returns (uint) { return x; } +} + +contract C { + function g() public { + D d = new D(); + uint y = d.f(); + assert(y == 0); // should fail in ext calls untrusted mode + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 8729: (124-131): Contract deployment is only supported in the trusted mode for external calls with the CHC engine. +// Warning 4661: (153-167): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/deployment/deploy_trusted.sol b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted.sol new file mode 100644 index 000000000000..388b0b0db835 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted.sol @@ -0,0 +1,16 @@ +contract D { + uint x; + function f() public view returns (uint) { return x; } +} + +contract C { + function g() public { + D d = new D(); + uint y = d.f(); + assert(y == 0); // should hold in ext calls trusted mode + } +} +// ==== +// SMTEngine: all +// SMTExtCalls: trusted +// ---- diff --git a/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_addresses.sol b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_addresses.sol new file mode 100644 index 000000000000..cf43dbda20d8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_addresses.sol @@ -0,0 +1,19 @@ +contract D { + uint x; +} + +contract C { + function f() public { + D d1 = new D(); + D d2 = new D(); + + assert(d1 != d2); // should hold in ext calls trusted mode + assert(address(this) != address(d1)); // should hold in ext calls trusted mode + assert(address(this) != address(d2)); // should hold in ext calls trusted mode + } +} +// ==== +// SMTEngine: all +// SMTExtCalls: trusted +// ---- +// Info 1180: Contract invariant(s) for :C:\n(:var 0).isActive[address(this)]\n diff --git a/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_flow.sol b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_flow.sol new file mode 100644 index 000000000000..ddd4b41e6895 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_flow.sol @@ -0,0 +1,32 @@ +contract D { + uint x; + function inc() public { ++x; } + function f() public view returns (uint) { return x; } +} + +contract C { + function f() public { + D d = new D(); + assert(d.f() == 0); // should hold + d.inc(); + assert(d.f() == 1); // should hold + d = new D(); + assert(d.f() == 0); // should hold + assert(d.f() == 1); // should fail + } +} +// ==== +// SMTEngine: all +// SMTExtCalls: trusted +// SMTIgnoreOS: macos +// ---- +// Warning 4984: (47-50): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 6328: (167-185): CHC: Assertion violation might happen here. +// Warning 6328: (215-233): CHC: Assertion violation might happen here. +// Warning 6328: (267-285): CHC: Assertion violation might happen here. +// Warning 6328: (304-322): CHC: Assertion violation might happen here. +// Warning 2661: (47-50): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4661: (167-185): BMC: Assertion violation happens here. +// Warning 4661: (215-233): BMC: Assertion violation happens here. +// Warning 4661: (267-285): BMC: Assertion violation happens here. +// Warning 4661: (304-322): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_keep_storage_constraints.sol b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_keep_storage_constraints.sol new file mode 100644 index 000000000000..b2aa7ac984c5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_keep_storage_constraints.sol @@ -0,0 +1,17 @@ +contract D { + uint x; +} + +contract C { + uint y; + function g() public { + D d = new D(); + assert(y == 0); // should hold + } +} +// ==== +// SMTEngine: all +// SMTExtCalls: trusted +// ---- +// Warning 2072: (72-75): Unused local variable. +// Info 1180: Contract invariant(s) for :C:\n(y <= 0)\n diff --git a/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_state_flow.sol b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_state_flow.sol new file mode 100644 index 000000000000..4b3367b11cf6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_state_flow.sol @@ -0,0 +1,24 @@ +contract D { + uint x; + function inc() public { ++x; } + function f() public view returns (uint) { return x; } +} + +contract C { + D d; + constructor() { + d = new D(); + assert(d.f() == 0); // should hold + } + function g() public view { + assert(d.f() == 0); // should fail + } +} +// ==== +// SMTEngine: all +// SMTExtCalls: trusted +// SMTIgnoreOS: macos +// ---- +// Warning 4984: (47-50): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 6328: (233-251): CHC: Assertion violation happens here. +// Warning 2661: (47-50): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. diff --git a/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_state_flow_2.sol b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_state_flow_2.sol new file mode 100644 index 000000000000..66684df8c153 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_state_flow_2.sol @@ -0,0 +1,20 @@ +contract D { + uint x; + function f() public view returns (uint) { return x; } +} + +contract C { + D d; + constructor() { + d = new D(); + assert(d.f() == 0); // should hold + } + function g() public view { + assert(d.f() == 0); // should hold + } +} +// ==== +// SMTEngine: all +// SMTExtCalls: trusted +// ---- +// Info 1180: Contract invariant(s) for :C:\n((:var 1).storage.storage_D_12[d].x_3_D_12 <= 0)\nReentrancy property(ies) for :D:\n((x' <= 0) || !(x <= 0))\n diff --git a/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_state_flow_3.sol b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_state_flow_3.sol new file mode 100644 index 000000000000..260909b87a25 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_state_flow_3.sol @@ -0,0 +1,21 @@ +contract D { + uint x; + function s(uint _x) public { x = _x; } + function f() public view returns (uint) { return x; } +} + +contract C { + D d; + constructor() { + d = new D(); + } + function g() public view { + assert(d.f() == 0); // should fail + } +} +// ==== +// SMTContract: C +// SMTEngine: all +// SMTExtCalls: trusted +// ---- +// Warning 6328: (204-222): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_state_flow_4.sol b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_state_flow_4.sol new file mode 100644 index 000000000000..5bd27c3b822d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_state_flow_4.sol @@ -0,0 +1,20 @@ +contract D { + bool b; + function s() public { b = true; } + function f() public view returns (bool) { return b; } +} + +contract C { + D d; + constructor() { + d = new D(); + } + function g() public view { + assert(d.f()); // should fail + } +} +// ==== +// SMTEngine: all +// SMTExtCalls: trusted +// ---- +// Warning 6328: (199-212): CHC: Assertion violation happens here.\nCounterexample:\nd = (- 1)\n\nTransaction trace:\nC.constructor()\nState: d = (- 1)\nC.g()\n D.f() -- trusted external call diff --git a/test/libsolidity/smtCheckerTests/deployment/deploy_untrusted.sol b/test/libsolidity/smtCheckerTests/deployment/deploy_untrusted.sol new file mode 100644 index 000000000000..a28d4621054f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/deployment/deploy_untrusted.sol @@ -0,0 +1,17 @@ +contract D { + uint x; + function f() public view returns (uint) { return x; } +} + +contract C { + function g() public { + D d = new D(); + uint y = d.f(); + assert(y == 0); // should fail in ext calls untrusted mode + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 8729: (124-131): Contract deployment is only supported in the trusted mode for external calls with the CHC engine. +// Warning 6328: (153-167): CHC: Assertion violation happens here.\nCounterexample:\n\nd = 0\ny = 1\n\nTransaction trace:\nC.constructor()\nC.g()\n d.f() -- untrusted external call diff --git a/test/libsolidity/smtCheckerTests/deployment/deploy_untrusted_addresses.sol b/test/libsolidity/smtCheckerTests/deployment/deploy_untrusted_addresses.sol new file mode 100644 index 000000000000..c934e152b27c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/deployment/deploy_untrusted_addresses.sol @@ -0,0 +1,22 @@ +contract D { + uint x; +} + +contract C { + function f() public { + D d1 = new D(); + D d2 = new D(); + + assert(d1 != d2); // should fail in ext calls untrusted mode + assert(address(this) != address(d1)); // should fail in ext calls untrusted mode + assert(address(this) != address(d2)); // should fail in ext calls untrusted mode + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 8729: (70-77): Contract deployment is only supported in the trusted mode for external calls with the CHC engine. +// Warning 8729: (88-95): Contract deployment is only supported in the trusted mode for external calls with the CHC engine. +// Warning 6328: (100-116): CHC: Assertion violation happens here.\nCounterexample:\n\nd1 = 0\nd2 = 0\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (163-199): CHC: Assertion violation happens here.\nCounterexample:\n\nd1 = 21238\nd2 = 21238\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (246-282): CHC: Assertion violation happens here.\nCounterexample:\n\nd1 = 21238\nd2 = 21238\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/deployment/deploy_untrusted_erase_storage_constraints.sol b/test/libsolidity/smtCheckerTests/deployment/deploy_untrusted_erase_storage_constraints.sol new file mode 100644 index 000000000000..be18b33f633a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/deployment/deploy_untrusted_erase_storage_constraints.sol @@ -0,0 +1,17 @@ +contract D { + uint x; +} + +contract C { + uint y; + function g() public { + D d = new D(); + assert(y == 0); // should fail in ext calls untrusted mode + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 2072: (72-75): Unused local variable. +// Warning 8729: (78-85): Contract deployment is only supported in the trusted mode for external calls with the CHC engine. +// Warning 6328: (89-103): CHC: Assertion violation happens here.\nCounterexample:\ny = 1\nd = 0\n\nTransaction trace:\nC.constructor()\nState: y = 0\nC.g() diff --git a/test/libsolidity/smtCheckerTests/deployment/deployment_trusted_with_value_1.sol b/test/libsolidity/smtCheckerTests/deployment/deployment_trusted_with_value_1.sol new file mode 100644 index 000000000000..92195221e2e0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/deployment/deployment_trusted_with_value_1.sol @@ -0,0 +1,20 @@ +contract A { + constructor() payable {} +} + +contract B { + function f() public payable { + require(address(this).balance == 100); + A a = new A{value: 50}(); + assert(address(this).balance == 50); // should hold + assert(address(this).balance == 60); // should fail + assert(address(a).balance >= 50); // should hold + assert(address(a).balance == 50); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// ---- +// Warning 6328: (211-246): CHC: Assertion violation happens here. +// Warning 6328: (316-348): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/call_abstract_constructor_trusted_1.sol b/test/libsolidity/smtCheckerTests/external_calls/call_abstract_constructor_trusted_1.sol new file mode 100644 index 000000000000..7905d5b4fb40 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/call_abstract_constructor_trusted_1.sol @@ -0,0 +1,28 @@ +contract D { + constructor(uint _x) { x = _x; } + uint public x; +} + +contract E { + constructor() { x = 2; } + uint public x; +} + +contract C { + constructor() { + address d = address(new D(42)); + assert(D(d).x() == 42); // should hold + assert(D(d).x() == 43); // should fail + uint y = E(d).x(); + assert(y == 2); // should fail, it would still call D.x() == 42 + assert(y == 42); // should hold, but fails due to false positive + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (231-253): CHC: Assertion violation happens here. +// Warning 6328: (293-307): CHC: Assertion violation happens here. +// Warning 6328: (359-374): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/call_abstract_constructor_trusted_2.sol b/test/libsolidity/smtCheckerTests/external_calls/call_abstract_constructor_trusted_2.sol new file mode 100644 index 000000000000..25cccead47d6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/call_abstract_constructor_trusted_2.sol @@ -0,0 +1,25 @@ +contract D { + constructor(uint _x) { x = _x; } + function setD(uint _x) public { x = _x; } + uint public x; +} + +contract C { + constructor() { + address d = address(new D(42)); + assert(D(d).x() == 42); // should hold + assert(D(d).x() == 21); // should fail + d.call(abi.encodeCall(D.setD, (21))); + assert(D(d).x() == 21); // should hold, but false positive cus low level calls are not handled precisely + assert(D(d).x() == 42); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTIgnoreCex: yes +// ---- +// Warning 9302: (257-293): Return value of low-level calls not used. +// Warning 6328: (216-238): CHC: Assertion violation happens here. +// Warning 6328: (297-319): CHC: Assertion violation happens here. +// Warning 6328: (404-426): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/call_abstract_trusted_1.sol b/test/libsolidity/smtCheckerTests/external_calls/call_abstract_trusted_1.sol new file mode 100644 index 000000000000..2dc5dd0fb87f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/call_abstract_trusted_1.sol @@ -0,0 +1,28 @@ +contract D { + constructor(uint _x) { x = _x; } + uint public x; +} + +contract E { + constructor() { x = 2; } + uint public x; +} + +contract C { + function f() public { + address d = address(new D(42)); + assert(D(d).x() == 42); // should hold + assert(D(d).x() == 43); // should fail + uint y = E(d).x(); + assert(y == 2); // should fail, it would still call D.x() == 42 + assert(y == 42); // should hold, but fails due to false positive + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (237-259): CHC: Assertion violation happens here. +// Warning 6328: (299-313): CHC: Assertion violation happens here. +// Warning 6328: (365-380): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/call_abstract_trusted_2.sol b/test/libsolidity/smtCheckerTests/external_calls/call_abstract_trusted_2.sol new file mode 100644 index 000000000000..9c36bfc74fc5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/call_abstract_trusted_2.sol @@ -0,0 +1,25 @@ +contract D { + constructor(uint _x) { x = _x; } + function setD(uint _x) public { x = _x; } + uint public x; +} + +contract C { + function f() public { + address d = address(new D(42)); + assert(D(d).x() == 42); // should hold + assert(D(d).x() == 21); // should fail + d.call(abi.encodeCall(D.setD, (21))); + assert(D(d).x() == 21); // should hold, but false positive cus low level calls are not handled precisely + assert(D(d).x() == 42); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTIgnoreCex: yes +// ---- +// Warning 9302: (263-299): Return value of low-level calls not used. +// Warning 6328: (222-244): CHC: Assertion violation happens here. +// Warning 6328: (303-325): CHC: Assertion violation happens here. +// Warning 6328: (410-432): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/call_abstract_trusted_3.sol b/test/libsolidity/smtCheckerTests/external_calls/call_abstract_trusted_3.sol new file mode 100644 index 000000000000..317fde003c35 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/call_abstract_trusted_3.sol @@ -0,0 +1,29 @@ +contract D { + constructor(uint _x) { x = _x; } + function setD(uint _x) public { x = _x; } + uint public x; +} + +contract C { + uint x; + + function f() public { + x = 666; + address d = address(new D(42)); + assert(D(d).x() == 42); // should hold + assert(D(d).x() == 21); // should fail + d.call(abi.encodeCall(D.setD, (21))); + assert(D(d).x() == 21); // should hold, but false positive cus low level calls are not handled precisely + assert(D(d).x() == 42); // should fail + assert(x == 666); // should hold, C's storage should not have been havoced + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTIgnoreCex: yes +// ---- +// Warning 9302: (284-320): Return value of low-level calls not used. +// Warning 6328: (243-265): CHC: Assertion violation happens here. +// Warning 6328: (324-346): CHC: Assertion violation happens here. +// Warning 6328: (431-453): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/call_reentrancy_view.sol b/test/libsolidity/smtCheckerTests/external_calls/call_reentrancy_view.sol index 87a9e2261459..f0f844fff380 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/call_reentrancy_view.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/call_reentrancy_view.sol @@ -15,4 +15,4 @@ contract C { // Warning 2519: (106-112): This declaration shadows an existing declaration. // Warning 2072: (106-112): Unused local variable. // Warning 2072: (114-131): Unused local variable. -// Info 1180: Contract invariant(s) for :C:\n(x <= 0)\nReentrancy property(ies) for :C:\n((!(x <= 0) || (x' <= 0)) && (!(x <= 0) || ( <= 0)))\n = 0 -> no errors\n = 1 -> Assertion failed at assert(x == 0)\n +// Info 1180: Contract invariant(s) for :C:\n(x <= 0)\nReentrancy property(ies) for :C:\n((!(x <= 0) || (x' <= 0)) && (( <= 0) || !(x <= 0)))\n = 0 -> no errors\n = 1 -> Assertion failed at assert(x == 0)\n diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_1_trusted.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_1_trusted.sol new file mode 100644 index 000000000000..8c7b589d1a99 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_1_trusted.sol @@ -0,0 +1,21 @@ +contract State { + function f(uint _x) public pure returns (uint) { + assert(_x < 100); // should fail + return _x; + } +} +contract C { + State s; + uint z = s.f(2); + + function f() public view { + assert(z == 2); // should hold in trusted mode + } +} +// ==== +// SMTContract: C +// SMTEngine: all +// SMTExtCalls: trusted +// SMTIgnoreInv: yes +// ---- +// Warning 6328: (69-85): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 100\n = 0\n\nTransaction trace:\nState.constructor()\nState.f(100) diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_2_trusted.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_2_trusted.sol new file mode 100644 index 000000000000..a8142272920f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_2_trusted.sol @@ -0,0 +1,17 @@ +contract C { + uint z = this.g(2); + + function g(uint _x) public pure returns (uint) { + assert(_x > 0); // should fail + return _x; + } + + function f() public view { + assert(z == 2); // should hold + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (87-101): CHC: Assertion violation happens here.\nCounterexample:\nz = 2\n_x = 0\n = 0\n\nTransaction trace:\nC.constructor()\nState: z = 2\nC.g(0) +// Info 1180: Contract invariant(s) for :C:\n(!(z >= 3) && !(z <= 1))\n diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_3_trusted.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_3_trusted.sol new file mode 100644 index 000000000000..8ff241e729da --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_from_constructor_3_trusted.sol @@ -0,0 +1,25 @@ +contract State { + function f(uint _x) public pure returns (uint) { + assert(_x < 100); // should fail + return _x; + } +} +contract C { + State s; + uint z; + + constructor() { + z = s.f(2); + } + + function f() public view { + assert(z == 2); // should hold in trusted mode + } +} +// ==== +// SMTContract: C +// SMTEngine: all +// SMTExtCalls: trusted +// ---- +// Warning 6328: (69-85): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 100\n = 0\n\nTransaction trace:\nState.constructor()\nState.f(100) +// Info 1180: Contract invariant(s) for :C:\n(!(z >= 3) && !(z <= 1))\n diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_1.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_1.sol new file mode 100644 index 000000000000..b7a161ac934a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_1.sol @@ -0,0 +1,41 @@ +contract A { + uint x; + function setX(uint _x) public { + x = _x; + } + function getX() public view returns (uint) { + return x; + } +} + +contract B { + A a; + constructor() { + a = new A(); + assert(a.getX() == 0); // should hold + } + function g() public view { + assert(a.getX() == 0); // should fail because A.setX() can be called without B + } + function getX() public view returns (uint) { + return a.getX(); + } +} + +contract C { + B b; + constructor() { + b = new B(); + assert(b.getX() == 0); // should hold + } + function f() public view { + assert(b.getX() == 0); // should fail because A.setX() can be called without A + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTIgnoreOS: macos +// ---- +// Warning 6328: (256-277): CHC: Assertion violation happens here. +// Warning 6328: (533-554): CHC: Assertion violation might happen here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_2.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_2.sol new file mode 100644 index 000000000000..e65d1b1e744e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_2.sol @@ -0,0 +1,52 @@ +contract A { + uint x; + address immutable owner; + constructor() { + owner = msg.sender; + } + function setX(uint _x) public { + require(msg.sender == owner); + x = _x; + } + function getX() public view returns (uint) { + return x; + } +} + +contract B { + A a; + constructor() { + a = new A(); + assert(a.getX() == 0); // should hold + } + function g() public view { + assert(a.getX() == 0); // should hold, but fails because + // the nondet_interface constraint added for `A a` in between + // txs of `B` does not have the constraint that `msg.sender != address(this)` + // so `A.setX` is allowed with `msg.sender = address(this)` inside + // the current rules defining nondet_interface. + // If we want to support that, we likely need a new type of nondet_interface + // `nondet_interface_with_tx` that contains tx data as well as restricts + // every further `nondet_interface_with_tx` to not have that `msg.sender`. + } + function getX() public view returns (uint) { + return a.getX(); + } +} + +contract C { + B b; + constructor() { + b = new B(); + assert(b.getX() == 0); // should hold + } + function f() public view { + assert(b.getX() == 0); // should hold + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// ---- +// Warning 6328: (434-455): CHC: Assertion violation happens here. +// Warning 6328: (1270-1291): CHC: Assertion violation might happen here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_3.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_3.sol new file mode 100644 index 000000000000..60bfd1660ad6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_3.sol @@ -0,0 +1,45 @@ +contract A { + uint x; + address immutable owner; + constructor() { + owner = msg.sender; + } + function setX(uint _x) public { + require(msg.sender == owner); + x = _x; + } + function getX() public view returns (uint) { + return x; + } +} + +contract B { + A a; + constructor() { + a = new A(); + assert(a.getX() == 0); // should hold + } + function g() public { + a.setX(42); + } + function getX() public view returns (uint) { + return a.getX(); + } +} + +contract C { + B b; + constructor() { + b = new B(); + assert(b.getX() == 0); // should hold + } + function f() public view { + assert(b.getX() == 0); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTIgnoreOS: macos +// ---- +// Warning 6328: (561-582): CHC: Assertion violation might happen here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_4.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_4.sol new file mode 100644 index 000000000000..4e86c7b2b59b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_4.sol @@ -0,0 +1,48 @@ +contract A { + uint x; + address immutable owner; + constructor() { + owner = msg.sender; + } + function setX(uint _x) public { + require(msg.sender == owner); + x = _x; + } + function getX() public view returns (uint) { + return x; + } +} + +contract B { + A a; + address immutable owner; + constructor() { + owner = msg.sender; + a = new A(); + assert(a.getX() == 0); // should hold + } + function g() public { + require(msg.sender == owner); + a.setX(42); + } + function getX() public view returns (uint) { + return a.getX(); + } +} + +contract C { + B b; + constructor() { + b = new B(); + assert(b.getX() == 0); // should hold + } + function f() public view { + assert(b.getX() == 0); // should hold + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTIgnoreOS: macos +// ---- +// Warning 6328: (641-662): CHC: Assertion violation might happen here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_5.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_5.sol new file mode 100644 index 000000000000..54566b85f4dc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_5.sol @@ -0,0 +1,50 @@ +contract A { + uint x; + address immutable owner; + constructor() { + owner = msg.sender; + } + function setX(uint _x) public { + require(msg.sender == owner); + x = _x; + } + function getX() public view returns (uint) { + return x; + } +} + +contract B { + A a; + address immutable owner; + constructor() { + owner = msg.sender; + a = new A(); + } + function g() public { + require(msg.sender == owner); + a.setX(42); + } + function getX() public view returns (uint) { + return a.getX(); + } +} + +contract C { + B b; + constructor() { + b = new B(); + assert(b.getX() == 0); // should hold + } + function f() public view { + assert(b.getX() == 0); // should fail + } + function h() public { + b.g(); + } +} +// ==== +// SMTContract: C +// SMTEngine: chc +// SMTExtCalls: trusted +// ---- +// Warning 6328: (601-622): CHC: Assertion violation might happen here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_semantic_this_1.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_semantic_this_1.sol new file mode 100644 index 000000000000..582d967f3f8b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_semantic_this_1.sol @@ -0,0 +1,17 @@ +contract C { + uint x; + function i() public { ++x; } + function f() public { + x = 0; + this.i(); + assert(x == 1); // should hold in trusted mode + assert(x != 1); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (147-161): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nC.constructor()\nState: x = 0\nC.f()\n C.i() -- trusted external call diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_semantic_this_2.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_semantic_this_2.sol new file mode 100644 index 000000000000..0f05a8b731e8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_semantic_this_2.sol @@ -0,0 +1,16 @@ +contract C { + uint x; + function i() public { ++x; } + function f() public { + x = 0; + ((this)).i(); + assert(x == 1); // should hold in trusted mode + assert(x != 1); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (151-165): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nC.constructor()\nState: x = 0\nC.f()\n C.i() -- trusted external call diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_semantic_this_3.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_semantic_this_3.sol new file mode 100644 index 000000000000..1b95859af850 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_semantic_this_3.sol @@ -0,0 +1,17 @@ +contract C { + uint x; + function i() public { ++x; } + function f() public { + x = 0; + C c = this; + c.i(); + assert(x == 1); // should hold in trusted mode + assert(x != 1); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (158-172): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_array_struct_trusted_1.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_array_struct_trusted_1.sol new file mode 100644 index 000000000000..c4bab9c759c0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_array_struct_trusted_1.sol @@ -0,0 +1,23 @@ +contract D { + uint public x; +} + +contract C { + struct S { + address d; + } + S[] ss; + constructor() { + ss.push(S(address(new D()))); + assert(D(ss[0].d).x() == 0); // should hold + } + function f() public view { + assert(D(ss[0].d).x() == 0); // should hold, but fails because we havoc the state + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (210-237): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_array_struct_trusted_2.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_array_struct_trusted_2.sol new file mode 100644 index 000000000000..522c840f5b08 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_array_struct_trusted_2.sol @@ -0,0 +1,24 @@ +contract D { + uint public x; + function setD(uint _x) public { x = _x; } +} + +contract C { + struct S { + address d; + } + S[] ss; + constructor() { + ss.push(S(address(new D()))); + assert(D(ss[0].d).x() == 0); // should hold + } + function f() public view { + assert(D(ss[0].d).x() == 0); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (253-280): CHC: Assertion violation happens here.\nCounterexample:\nss = [{d: 0x4706}]\n\nTransaction trace:\nC.constructor()\nState: ss = [{d: 0x4706}]\nC.f() diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_array_trusted_1.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_array_trusted_1.sol new file mode 100644 index 000000000000..541999b9f90e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_array_trusted_1.sol @@ -0,0 +1,21 @@ +contract D { + uint public x; + function setD(uint _x) public { x = _x; } +} + +contract C { + address[] ds; + constructor() { + ds.push(address(new D())); + assert(D(ds[0]).x() == 0); // should hold + } + function f() public view { + assert(D(ds[0]).x() == 0); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (226-251): CHC: Assertion violation happens here.\nCounterexample:\nds = [0x0]\n\nTransaction trace:\nC.constructor()\nState: ds = [0x0]\nC.f() diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_array_trusted_2.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_array_trusted_2.sol new file mode 100644 index 000000000000..f7e31bf74af1 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_array_trusted_2.sol @@ -0,0 +1,20 @@ +contract D { + uint public x; +} + +contract C { + address[] ds; + constructor() { + ds.push(address(new D())); + assert(D(ds[0]).x() == 0); // should hold + } + function f() public view { + assert(D(ds[0]).x() == 0); // should hold, but fails because we havoc the state + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (183-208): CHC: Assertion violation happens here.\nCounterexample:\nds = [0x25]\n\nTransaction trace:\nC.constructor()\nState: ds = [0x25]\nC.f() diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_struct_trusted_1.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_struct_trusted_1.sol new file mode 100644 index 000000000000..2ac70b33d371 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_struct_trusted_1.sol @@ -0,0 +1,24 @@ +contract D { + uint public x; + function setD(uint _x) public { x = _x; } +} + +contract C { + struct S { + address d; + } + S s; + constructor() { + s.d = address(new D()); + assert(D(s.d).x() == 0); // should hold + } + function f() public view { + assert(D(s.d).x() == 0); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (240-263): CHC: Assertion violation happens here.\nCounterexample:\ns = {d: 0x5039}\n\nTransaction trace:\nC.constructor()\nState: s = {d: 0x5039}\nC.f() diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_struct_trusted_2.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_struct_trusted_2.sol new file mode 100644 index 000000000000..e527c5bc0120 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_struct_trusted_2.sol @@ -0,0 +1,23 @@ +contract D { + uint public x; +} + +contract C { + struct S { + address d; + } + S s; + constructor() { + s.d = address(new D()); + assert(D(s.d).x() == 0); // should hold + } + function f() public view { + assert(D(s.d).x() == 0); // should hold, but fails because we havoc the state + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (197-220): CHC: Assertion violation happens here.\nCounterexample:\ns = {d: 0x5039}\n\nTransaction trace:\nC.constructor()\nState: s = {d: 0x5039}\nC.f() diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_struct_trusted_3.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_struct_trusted_3.sol new file mode 100644 index 000000000000..fff8a971849a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_struct_trusted_3.sol @@ -0,0 +1,26 @@ +contract D { + uint public x; +} + +contract C { + struct S { + address d; + } + struct T { + S s; + } + T t; + constructor() { + t.s.d = address(new D()); + assert(D(t.s.d).x() == 0); // should hold + } + function f() public view { + assert(D(t.s.d).x() == 0); // should hold, but fails because we havoc the state + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (223-248): CHC: Assertion violation happens here.\nCounterexample:\nt = {s: {d: 0x5039}}\n\nTransaction trace:\nC.constructor()\nState: t = {s: {d: 0x5039}}\nC.f() diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_struct_trusted_4.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_struct_trusted_4.sol new file mode 100644 index 000000000000..63d026e52b55 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_address_inside_struct_trusted_4.sol @@ -0,0 +1,27 @@ +contract D { + uint public x; + function setD(uint _x) public { x = _x; } +} + +contract C { + struct S { + address d; + } + struct T { + S s; + } + T t; + constructor() { + t.s.d = address(new D()); + assert(D(t.s.d).x() == 0); // should hold + } + function f() public view { + assert(D(t.s.d).x() == 0); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (266-291): CHC: Assertion violation happens here.\nCounterexample:\nt = {s: {d: 0x5039}}\n\nTransaction trace:\nC.constructor()\nState: t = {s: {d: 0x5039}}\nC.f() diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_array_struct_trusted_1.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_array_struct_trusted_1.sol new file mode 100644 index 000000000000..0b8cd8e59844 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_array_struct_trusted_1.sol @@ -0,0 +1,23 @@ +contract D { + uint public x; +} + +contract C { + struct S { + D d; + } + S[] ss; + constructor() { + ss.push(S(new D())); + assert(ss[0].d.x() == 0); // should hold + } + function f() public view { + assert(ss[0].d.x() == 0); // should hold, but fails because we havoc the state + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (192-216): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_array_struct_trusted_2.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_array_struct_trusted_2.sol new file mode 100644 index 000000000000..97f61fa0459d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_array_struct_trusted_2.sol @@ -0,0 +1,24 @@ +contract D { + uint public x; + function setD(uint _x) public { x = _x; } +} + +contract C { + struct S { + D d; + } + S[] ss; + constructor() { + ss.push(S(new D())); + assert(ss[0].d.x() == 0); // should hold + } + function f() public view { + assert(ss[0].d.x() == 0); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (235-259): CHC: Assertion violation happens here.\nCounterexample:\nss = [{d: 20819}]\n\nTransaction trace:\nC.constructor()\nState: ss = [{d: 20819}]\nC.f() diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_array_trusted_1.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_array_trusted_1.sol new file mode 100644 index 000000000000..475c391844bb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_array_trusted_1.sol @@ -0,0 +1,21 @@ +contract D { + uint public x; + function setD(uint _x) public { x = _x; } +} + +contract C { + D[] ds; + constructor() { + ds.push(new D()); + assert(ds[0].x() == 0); // should hold + } + function f() public view { + assert(ds[0].x() == 0); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (208-230): CHC: Assertion violation happens here.\nCounterexample:\nds = [39]\n\nTransaction trace:\nC.constructor()\nState: ds = [39]\nC.f() diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_array_trusted_2.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_array_trusted_2.sol new file mode 100644 index 000000000000..baeddda08852 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_array_trusted_2.sol @@ -0,0 +1,21 @@ +contract D { + uint public x; +} + +contract C { + D[] ds; + constructor() { + ds.push(new D()); + assert(ds[0].x() == 0); // should hold + } + function f() public view { + assert(ds[0].x() == 0); // should hold, but fails because we havoc the state + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (165-187): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_struct_trusted_1.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_struct_trusted_1.sol new file mode 100644 index 000000000000..293c223044ce --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_struct_trusted_1.sol @@ -0,0 +1,24 @@ +contract D { + uint public x; + function setD(uint _x) public { x = _x; } +} + +contract C { + struct S { + D d; + } + S s; + constructor() { + s.d = new D(); + assert(s.d.x() == 0); // should hold + } + function f() public view { + assert(s.d.x() == 0); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (222-242): CHC: Assertion violation happens here.\nCounterexample:\ns = {d: 20819}\n\nTransaction trace:\nC.constructor()\nState: s = {d: 20819}\nC.f() diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_struct_trusted_2.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_struct_trusted_2.sol new file mode 100644 index 000000000000..59381960c534 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_struct_trusted_2.sol @@ -0,0 +1,24 @@ +contract D { + uint public x; +} + +contract C { + struct S { + D d; + } + S s; + constructor() { + s.d = new D(); + assert(s.d.x() == 0); // should hold + } + function f() public view { + assert(s.d.x() == 0); // should hold, but fails because we havoc the state + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (179-199): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_struct_trusted_3.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_struct_trusted_3.sol new file mode 100644 index 000000000000..06cd20cfe35a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_struct_trusted_3.sol @@ -0,0 +1,27 @@ +contract D { + uint public x; +} + +contract C { + struct S { + D d; + } + struct T { + S s; + } + T t; + constructor() { + t.s.d = new D(); + assert(t.s.d.x() == 0); // should hold + } + function f() public view { + assert(t.s.d.x() == 0); // should hold, but fails because we havoc the state + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (205-227): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_struct_trusted_4.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_struct_trusted_4.sol new file mode 100644 index 000000000000..6a1fb227445b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_state_var_contract_inside_struct_trusted_4.sol @@ -0,0 +1,27 @@ +contract D { + uint public x; + function setD(uint _x) public { x = _x; } +} + +contract C { + struct S { + D d; + } + struct T { + S s; + } + T t; + constructor() { + t.s.d = new D(); + assert(t.s.d.x() == 0); // should hold + } + function f() public view { + assert(t.s.d.x() == 0); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (248-270): CHC: Assertion violation happens here.\nCounterexample:\nt = {s: {d: 20819}}\n\nTransaction trace:\nC.constructor()\nState: t = {s: {d: 20819}}\nC.f() diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_this_with_value_1.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_this_with_value_1.sol index 06390b12ca95..c52e0773fb82 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_call_this_with_value_1.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_this_with_value_1.sol @@ -11,4 +11,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (157-192): CHC: Assertion violation happens here. +// Warning 6328: (157-192): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.g()\n C.h() -- trusted external call diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_this_with_value_2.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_this_with_value_2.sol index fc2d844d9d50..f804078cd16c 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_call_this_with_value_2.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_this_with_value_2.sol @@ -1,7 +1,11 @@ contract C { function g(uint i) public { require(address(this).balance == 100); + // if called address is same as this, don't do anything with the value stuff + // or fix the receiving end this.h{value: i}(); + uint x = address(this).balance; + assert(x == 100); // should hold assert(address(this).balance == 100); // should hold assert(address(this).balance == 90); // should fail } @@ -12,4 +16,4 @@ contract C { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 6328: (162-197): CHC: Assertion violation happens here. +// Warning 6328: (340-375): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_pure_trusted.sol b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_pure_trusted.sol new file mode 100644 index 000000000000..6c634d4efbf6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_pure_trusted.sol @@ -0,0 +1,33 @@ +contract Crypto { + function hash(bytes32) external pure returns (bytes32) { + return bytes32(0); + } +} + +contract C { + address owner; + bytes32 sig_1; + bytes32 sig_2; + Crypto d; + + constructor() { + owner = msg.sender; + } + + function f1(bytes32 _msg) public { + address prevOwner = owner; + sig_1 = d.hash(_msg); + sig_2 = d.hash(_msg); + assert(prevOwner == owner); + } + + function inv() public view { + assert(sig_1 == sig_2); + } +} +// ==== +// SMTContract: C +// SMTEngine: all +// SMTExtCalls: trusted +// ---- +// Info 1180: Contract invariant(s) for :C:\n((sig_1 <= 0) && (sig_2 <= 0))\nReentrancy property(ies) for :Crypto:\n( = 0)\n = 0 -> no errors\n = 1 -> Assertion failed at assert(prevOwner == owner)\n = 3 -> Assertion failed at assert(sig_1 == sig_2)\n diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_2_trusted.sol b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_2_trusted.sol new file mode 100644 index 000000000000..62f53b64c20e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_2_trusted.sol @@ -0,0 +1,47 @@ +contract State { + C c; + constructor(C _c) { + c = _c; + } + function f() public view returns (uint) { + return c.g(); + } +} + +contract C { + address owner; + uint y; + uint z; + State s; + bool insidef; + + constructor() { + owner = msg.sender; + s = new State(this); + } + + function zz() public { + require(insidef); + z = 3; + } + + function f() public { + require(!insidef); + address prevOwner = owner; + insidef = true; + s.f(); + assert(z == y); + assert(prevOwner == owner); + insidef = false; + } + + function g() public view returns (uint) { + return y; + } +} +// ==== +// SMTContract: C +// SMTEngine: chc +// SMTExtCalls: trusted +// ---- +// Info 1180: Contract invariant(s) for :C:\n((y <= 0) && (insidef || (z <= 0)))\nReentrancy property(ies) for :State:\n( = 0)\n = 0 -> no errors\n = 2 -> Assertion failed at assert(z == y)\n = 3 -> Assertion failed at assert(prevOwner == owner)\n diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_indirect_trusted.sol b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_indirect_trusted.sol new file mode 100644 index 000000000000..8000b3267d9e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_indirect_trusted.sol @@ -0,0 +1,56 @@ +contract Other { + C c; + constructor(C _c) { + c = _c; + } + function h() public { + c.setOwner(address(0)); + } +} + +contract State { + uint x; + Other o; + C c; + constructor(C _c) { + c = _c; + o = new Other(_c); + } + function f() public returns (uint) { + o.h(); + return c.g(); + } +} + +contract C { + address owner; + uint y; + State s; + + constructor() { + owner = msg.sender; + s = new State(this); + } + + function setOwner(address _owner) public { + owner = _owner; + } + + function f() public { + address prevOwner = owner; + uint z = s.f(); + assert(z == y); // should hold + assert(prevOwner == owner); // should not hold because of reentrancy + } + + function g() public view returns (uint) { + return y; + } +} +// ==== +// SMTContract: C +// SMTEngine: chc +// SMTExtCalls: trusted +// ---- +// Warning 6328: (531-545): CHC: Assertion violation might happen here. +// Warning 6328: (564-590): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_trusted.sol b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_trusted.sol new file mode 100644 index 000000000000..ee373e293792 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_trusted.sol @@ -0,0 +1,38 @@ +contract State { + C c; + constructor(C _c) { + c = _c; + } + function f() public view returns (uint) { + return c.g(); + } +} + +contract C { + address owner; + uint y; + State s; + + constructor() { + owner = msg.sender; + s = new State(this); + } + + function f() public view { + address prevOwner = owner; + uint z = s.f(); + assert(z == y); + assert(prevOwner == owner); + } + + function g() public view returns (uint) { + return y; + } +} +// ==== +// SMTContract: C +// SMTEngine: chc +// SMTExtCalls: trusted +// ---- +// Warning 6328: (314-328): CHC: Assertion violation might happen here. +// Info 1180: Reentrancy property(ies) for :State:\n( = 0)\n = 0 -> no errors\n = 1 -> Assertion failed at assert(z == y)\n = 2 -> Assertion failed at assert(prevOwner == owner)\n diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_unsafe_trusted.sol b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_unsafe_trusted.sol new file mode 100644 index 000000000000..4d179ddf8254 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_unsafe_trusted.sol @@ -0,0 +1,44 @@ +contract State { + C c; + constructor(C _c) { + c = _c; + } + function f() public returns (uint) { + c.setOwner(address(0)); + return c.g(); + } +} + +contract C { + address owner; + uint y; + State s; + + constructor() { + owner = msg.sender; + s = new State(this); + } + + function setOwner(address _owner) public { + owner = _owner; + } + + function f() public { + address prevOwner = owner; + uint z = s.f(); + assert(z == y); // should hold + assert(prevOwner == owner); // should not hold because of reentrancy + } + + function g() public view returns (uint) { + return y; + } +} +// ==== +// SMTContract: C +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTIgnoreOS: macos +// ---- +// Warning 6328: (396-410): CHC: Assertion violation might happen here. +// Warning 6328: (429-455): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_trusted.sol b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_trusted.sol new file mode 100644 index 000000000000..1c557f7a6ddd --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_trusted.sol @@ -0,0 +1,36 @@ +contract State { + uint x; + function f() public returns (uint) { + if (x == 0) x = 1; + else if (x == 1) x = 2; + else if (x == 2) x = 0; + return x; + } +} + +contract C { + address owner; + uint y; + uint z; + State s; + + constructor() { + s = new State(); + owner = msg.sender; + } + + function f() public { + address prevOwner = owner; + y = s.f(); + z = s.f(); + assert(prevOwner == owner); + assert(y != z); + } +} +// ==== +// SMTContract: C +// SMTEngine: chc +// SMTExtCalls: trusted +// ---- +// Warning 6328: (355-381): CHC: Assertion violation might happen here. +// Warning 6328: (385-399): CHC: Assertion violation might happen here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_unsafe_trusted.sol b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_unsafe_trusted.sol new file mode 100644 index 000000000000..7d217e145a8b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_unsafe_trusted.sol @@ -0,0 +1,40 @@ +contract State { + uint x; + function f() public returns (uint) { + if (x == 0) x = 1; + else if (x == 1) x = 2; + else if (x == 2) x = 0; + return x; + } +} + +contract C { + address owner; + uint y; + uint z; + State s; + + constructor() { + owner = msg.sender; + s = new State(); + } + + function setOwner(address _owner) public { + owner = _owner; + } + + function f() public { + address prevOwner = owner; + y = s.f(); + z = s.f(); + assert(prevOwner == owner); + assert(y != z); + } +} +// ==== +// SMTContract: C +// SMTEngine: chc +// SMTExtCalls: trusted +// ---- +// Warning 6328: (421-447): CHC: Assertion violation might happen here. +// Warning 6328: (451-465): CHC: Assertion violation might happen here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_2.sol b/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_2.sol index 19ff2bbad86d..268db82b4632 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_2.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_2.sol @@ -13,4 +13,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (117-131): CHC: Assertion violation happens here.\nCounterexample:\nlocked = false\ntarget = 0x0\n\nTransaction trace:\nC.constructor()\nState: locked = true\nC.call(0x0)\n D(target).e() -- untrusted external call, synthesized as:\n C.call(0x0) -- reentrant call +// Warning 6328: (117-131): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_safe.sol b/test/libsolidity/smtCheckerTests/external_calls/external_safe.sol index b50185449b78..550e17b61927 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_safe.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_safe.sol @@ -18,3 +18,5 @@ contract C { // SMTEngine: all // SMTIgnoreOS: macos // ---- +// Warning 6328: (167-181): CHC: Assertion violation might happen here. +// Warning 4661: (167-181): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/token_trusted_transfer_correct.sol b/test/libsolidity/smtCheckerTests/external_calls/token_trusted_transfer_correct.sol new file mode 100644 index 000000000000..438feae21796 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/token_trusted_transfer_correct.sol @@ -0,0 +1,64 @@ +interface Token { + function balanceOf(address _a) external view returns (uint); + function transfer(address _to, uint _amt) external; +} + +contract TokenCorrect is Token { + mapping (address => uint) balance; + constructor(address _a, uint _b) { + balance[_a] = _b; + } + function balanceOf(address _a) public view override returns (uint) { + return balance[_a]; + } + function transfer(address _to, uint _amt) public override { + require(balance[msg.sender] >= _amt); + balance[msg.sender] -= _amt; + balance[_to] += _amt; + } +} + +contract Test { + function property_transfer(address _token, address _to, uint _amt) public { + require(_to != address(this)); + + TokenCorrect t = TokenCorrect(_token); + + uint xPre = t.balanceOf(address(this)); + require(xPre >= _amt); + uint yPre = t.balanceOf(_to); + + t.transfer(_to, _amt); + uint xPost = t.balanceOf(address(this)); + uint yPost = t.balanceOf(_to); + + assert(xPost == xPre - _amt); + assert(yPost == yPre + _amt); + } + + function test_concrete() public { + TokenCorrect t = new TokenCorrect(address(this), 1000); + + uint b = t.balanceOf(address(this)); + assert(b == 1000); + + address other = address(0x333); + require(address(this) != other); + + uint c = t.balanceOf(other); + assert(c == 0); + + t.transfer(other, 100); + + uint d = t.balanceOf(address(this)); + assert(d == 900); + + uint e = t.balanceOf(other); + assert(e == 100); + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTContract: Test +// SMTTargets: assert diff --git a/test/libsolidity/smtCheckerTests/external_calls/token_trusted_transfer_wrong.sol b/test/libsolidity/smtCheckerTests/external_calls/token_trusted_transfer_wrong.sol new file mode 100644 index 000000000000..6bb6074b2062 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/token_trusted_transfer_wrong.sol @@ -0,0 +1,68 @@ +interface Token { + function balanceOf(address _a) external view returns (uint); + function transfer(address _to, uint _amt) external; +} + +contract TokenWrong is Token { + mapping (address => uint) balance; + constructor(address _a, uint _b) { + balance[_a] = _b; + } + function balanceOf(address _a) public view override returns (uint) { + return balance[_a]; + } + function transfer(address _to, uint _amt) public override { + require(balance[msg.sender] >= _amt); + // Commented out to make this token implementation wrong. + //balance[msg.sender] -= _amt; + balance[_to] += _amt; + } +} + +contract Test { + function property_transfer(address _token, address _to, uint _amt) public { + require(_to != address(this)); + + TokenWrong t = TokenWrong(_token); + + uint xPre = t.balanceOf(address(this)); + require(xPre >= _amt); + uint yPre = t.balanceOf(_to); + + t.transfer(_to, _amt); + uint xPost = t.balanceOf(address(this)); + uint yPost = t.balanceOf(_to); + + assert(xPost == xPre - _amt); // should fail + assert(yPost == yPre + _amt); + } + + function test_concrete() public { + TokenWrong t = new TokenWrong(address(this), 1000); + + uint b = t.balanceOf(address(this)); + assert(b == 1000); + + address other = address(0x333); + require(address(this) != other); + + uint c = t.balanceOf(other); + assert(c == 0); + + t.transfer(other, 100); + + uint d = t.balanceOf(address(this)); + assert(d == 900); // should fail + + uint e = t.balanceOf(other); + assert(e == 100); + } +} +// ==== +// SMTContract: Test +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (950-978): CHC: Assertion violation happens here. +// Warning 6328: (1370-1386): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/file_level/new_operator.sol b/test/libsolidity/smtCheckerTests/file_level/new_operator.sol index 8a0f118ae7c1..925f53bc9c01 100644 --- a/test/libsolidity/smtCheckerTests/file_level/new_operator.sol +++ b/test/libsolidity/smtCheckerTests/file_level/new_operator.sol @@ -14,5 +14,5 @@ contract D { // ==== // SMTEngine: all // ---- -// Warning 4588: (78-85): Assertion checker does not yet implement this type of function call. +// Warning 8729: (78-85): Contract deployment is only supported in the trusted mode for external calls with the CHC engine. // Warning 6328: (133-152): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nD.constructor()\nD.f()\n test() -- internal call\n (new C()).x() -- untrusted external call diff --git a/test/libsolidity/smtCheckerTests/functions/functions_external_2.sol b/test/libsolidity/smtCheckerTests/functions/functions_external_2.sol index 5efcb2da1b9d..3a3b295e450c 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_external_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_external_2.sol @@ -25,4 +25,4 @@ contract C // SMTIgnoreOS: macos // ---- // Warning 6328: (234-253): CHC: Assertion violation happens here. -// Info 1180: Reentrancy property(ies) for :C:\n!( = 1)\n((!((map[1] + ((- 1) * map[0])) <= 0) || ((map'[1] + ((- 1) * map'[0])) <= 0)) && !( = 2) && (!((map[1] + ((- 1) * map[0])) >= 0) || ((map'[0] + ((- 1) * map'[1])) <= 0)))\n = 0 -> no errors\n = 1 -> Assertion failed at assert(map[0] == map[1])\n = 2 -> Assertion failed at assert(map[0] == map[1])\n = 3 -> Assertion failed at assert(map[0] == 0)\n +// Info 1180: Reentrancy property(ies) for :C:\n!( = 1)\n((!((map[1] + ((- 1) * map[0])) >= 0) || ((map'[0] + ((- 1) * map'[1])) <= 0)) && !( = 2) && (!((map[1] + ((- 1) * map[0])) <= 0) || ((map'[1] + ((- 1) * map'[0])) <= 0)))\n = 0 -> no errors\n = 1 -> Assertion failed at assert(map[0] == map[1])\n = 2 -> Assertion failed at assert(map[0] == map[1])\n = 3 -> Assertion failed at assert(map[0] == 0)\n diff --git a/test/libsolidity/smtCheckerTests/functions/getters/external_getter_1.sol b/test/libsolidity/smtCheckerTests/functions/getters/external_getter_1.sol new file mode 100644 index 000000000000..00bfb35ca2c3 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/external_getter_1.sol @@ -0,0 +1,22 @@ +contract D { + uint public d; + function g() public { + ++d; + } +} + +contract C { + function f() public { + D a = new D(); + assert(a.d() == 0); // should hold + a.g(); + assert(a.d() == 1); // should hold + assert(a.d() == 0); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (203-221): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/getters/external_getter_2.sol b/test/libsolidity/smtCheckerTests/functions/getters/external_getter_2.sol new file mode 100644 index 000000000000..5da279fc9586 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/external_getter_2.sol @@ -0,0 +1,33 @@ +contract E { + uint public e; + function setE(uint _e) public { + e = _e; + } +} + +contract D { + E e; + constructor(E _e) { + e = _e; + } + function setE(uint x) public { + e.setE(x); + } +} + +contract C { + function f() public { + E e = new E(); + D d = new D(e); + assert(e.e() == 0); // should hold + d.setE(42); + assert(e.e() == 42); // should hold + assert(e.e() == 2); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// ---- +// Warning 6328: (344-362): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/getters/external_getter_this_1.sol b/test/libsolidity/smtCheckerTests/functions/getters/external_getter_this_1.sol new file mode 100644 index 000000000000..b7673203e1ee --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/external_getter_this_1.sol @@ -0,0 +1,18 @@ +contract C { + uint public x; + + function f() public { + x = 2; + x = 3; + uint y = this.x(); + assert(y == 3); // should hold + assert(y == 2); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (127-141): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/getters/external_getter_this_2.sol b/test/libsolidity/smtCheckerTests/functions/getters/external_getter_this_2.sol new file mode 100644 index 000000000000..b3dfe04adeed --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/external_getter_this_2.sol @@ -0,0 +1,19 @@ +contract C { + uint public x; + + function f() public { + x = 2; + x = 3; + C c = this; + uint y = c.x(); + assert(y == 3); // should hold + assert(y == 2); // should fail + } +} +// ==== +// SMTEngine: chc +// SMTExtCalls: trusted +// SMTTargets: assert +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (138-152): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/virtual_function_called_by_constructor.sol b/test/libsolidity/smtCheckerTests/functions/virtual_function_called_by_constructor.sol index 0117f9b49d8d..e30a5e743801 100644 --- a/test/libsolidity/smtCheckerTests/functions/virtual_function_called_by_constructor.sol +++ b/test/libsolidity/smtCheckerTests/functions/virtual_function_called_by_constructor.sol @@ -27,4 +27,4 @@ contract C is A { // ---- // Warning 6328: (199-214): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\nTransaction trace:\nA.constructor()\nState: x = 2\nA.i() // Warning 6328: (387-401): CHC: Assertion violation happens here.\nCounterexample:\nx = 10\n\nTransaction trace:\nC.constructor()\nState: x = 10\nC.i() -// Info 1180: Contract invariant(s) for :A:\n(!(x <= 1) && !(x >= 3))\nContract invariant(s) for :C:\n(!(x >= 11) && !(x <= 9))\n +// Info 1180: Contract invariant(s) for :A:\n(!(x <= 1) && !(x >= 3))\nContract invariant(s) for :C:\n(!(x <= 9) && !(x >= 11))\n diff --git a/test/libsolidity/smtCheckerTests/imports/ExtCall.sol b/test/libsolidity/smtCheckerTests/imports/ExtCall.sol index 047148a90cce..7675bc42ea8c 100644 --- a/test/libsolidity/smtCheckerTests/imports/ExtCall.sol +++ b/test/libsolidity/smtCheckerTests/imports/ExtCall.sol @@ -39,4 +39,4 @@ contract ExtCallTest { // SMTIgnoreCex: yes // ---- // Warning 6328: (ExtCall.sol:362-381): CHC: Assertion violation happens here. -// Warning 4588: (ExtCall.t.sol:110-123): Assertion checker does not yet implement this type of function call. +// Warning 8729: (ExtCall.t.sol:110-123): Contract deployment is only supported in the trusted mode for external calls with the CHC engine. diff --git a/test/libsolidity/smtCheckerTests/imports/import_as_module_2.sol b/test/libsolidity/smtCheckerTests/imports/import_as_module_2.sol index 8ddef2da9e0e..1298f1f09762 100644 --- a/test/libsolidity/smtCheckerTests/imports/import_as_module_2.sol +++ b/test/libsolidity/smtCheckerTests/imports/import_as_module_2.sol @@ -17,6 +17,7 @@ function f(uint _x) pure { } // ==== // SMTEngine: all +// SMTIgnoreOS: macos // ---- // Warning 6328: (A:50-64): CHC: Assertion violation happens here.\nCounterexample:\n\n_y = 0\n\nTransaction trace:\nD.constructor()\nD.g(0)\n s1.sol:f(200) -- internal call\n s1.sol:f(0) -- internal call\n A:f(10) -- internal call\n A:f(0) -- internal call // Warning 6328: (s1.sol:28-44): CHC: Assertion violation happens here.\nCounterexample:\n\n_y = 0\n\nTransaction trace:\nD.constructor()\nD.g(0)\n s1.sol:f(200) -- internal call\n s1.sol:f(0) -- internal call diff --git a/test/libsolidity/smtCheckerTests/operators/compound_mul_mapping.sol b/test/libsolidity/smtCheckerTests/operators/compound_mul_mapping.sol index 42775cc9b273..b7b06b1fc810 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_mul_mapping.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_mul_mapping.sol @@ -11,5 +11,6 @@ contract C } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- -// Warning 6328: (164-183): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\np = 0\n\nTransaction trace:\nC.constructor()\nC.f(0, 0) +// Warning 6328: (164-183): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_6.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_6.sol index b26fcbd93c09..923147d4957f 100644 --- a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_6.sol +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_6.sol @@ -25,4 +25,4 @@ contract C { // SMTIgnoreOS: macos // ---- // Warning 2072: (255-261): Unused local variable. -// Info 1180: Reentrancy property(ies) for :C:\n((!(x' >= 3) || (a' = a)) && ( <= 0) && (!(x' <= 0) || !(x >= 2)) && (!(x <= 2) || !(x' >= 3)))\n = 0 -> no errors\n = 1 -> Assertion failed at assert(x == 2 || x == 1)\n +// Info 1180: Reentrancy property(ies) for :C:\n((!(x <= 2) || !(x' >= 3)) && ( <= 0) && (!(x' <= 0) || !(x >= 2)))\n = 0 -> no errors\n = 1 -> Assertion failed at assert(x == 2 || x == 1)\n diff --git a/test/libsolidity/smtCheckerTests/special/tx_vars_reentrancy_1.sol b/test/libsolidity/smtCheckerTests/special/tx_vars_reentrancy_1.sol index 0f511a05d1e4..7af8c96c894d 100644 --- a/test/libsolidity/smtCheckerTests/special/tx_vars_reentrancy_1.sol +++ b/test/libsolidity/smtCheckerTests/special/tx_vars_reentrancy_1.sol @@ -11,6 +11,7 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // SMTIgnoreOS: macos // ---- // Warning 6328: (135-169): CHC: Assertion violation happens here.\nCounterexample:\n\n_i = 0\nx = 5892\n\nTransaction trace:\nC.constructor()\nC.g(0){ msg.value: 11 }\n _i.f() -- untrusted external call, synthesized as:\n C.g(0){ msg.value: 32278 } -- reentrant call\n _i.f() -- untrusted external call diff --git a/test/libsolidity/smtCheckerTests/try_catch/try_new.sol b/test/libsolidity/smtCheckerTests/try_catch/try_new.sol index e7075e076550..5c98c279d2a0 100644 --- a/test/libsolidity/smtCheckerTests/try_catch/try_new.sol +++ b/test/libsolidity/smtCheckerTests/try_catch/try_new.sol @@ -28,5 +28,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 4588: (231-245): Assertion checker does not yet implement this type of function call. -// Warning 4588: (492-507): Assertion checker does not yet implement this type of function call. +// Warning 8729: (231-245): Contract deployment is only supported in the trusted mode for external calls with the CHC engine. +// Warning 8729: (492-507): Contract deployment is only supported in the trusted mode for external calls with the CHC engine. diff --git a/test/libsolidity/smtCheckerTests/typecast/bytes_to_fixed_bytes_1.sol b/test/libsolidity/smtCheckerTests/typecast/bytes_to_fixed_bytes_1.sol index 1d00e7fe3b47..ffd6304c98bd 100644 --- a/test/libsolidity/smtCheckerTests/typecast/bytes_to_fixed_bytes_1.sol +++ b/test/libsolidity/smtCheckerTests/typecast/bytes_to_fixed_bytes_1.sol @@ -11,4 +11,7 @@ contract C { assert(g == 0x0001020304050607080900010203040506070809000102030405060708090001); // should hold } } +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes // ---- diff --git a/test/libsolidity/smtCheckerTests/typecast/string_to_bytes_push_1.sol b/test/libsolidity/smtCheckerTests/typecast/string_to_bytes_push_1.sol index 4910c417e810..33546b49c029 100644 --- a/test/libsolidity/smtCheckerTests/typecast/string_to_bytes_push_1.sol +++ b/test/libsolidity/smtCheckerTests/typecast/string_to_bytes_push_1.sol @@ -11,4 +11,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (132-160): CHC: Assertion violation happens here.\nCounterexample:\nx = [0x61, 0x62, 0x63, 0x61]\n\nTransaction trace:\nC.constructor()\nState: x = []\nC.s() +// Warning 6328: (132-160): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nState: x = []\nC.s() diff --git a/test/libsolidity/smtCheckerTests/types/bool_simple_2.sol b/test/libsolidity/smtCheckerTests/types/bool_simple_2.sol index 44daa8912840..7e23d77d65c9 100644 --- a/test/libsolidity/smtCheckerTests/types/bool_simple_2.sol +++ b/test/libsolidity/smtCheckerTests/types/bool_simple_2.sol @@ -5,5 +5,6 @@ contract C { } // ==== // SMTEngine: all +// SMTIgnoreCex: yes // ---- // Warning 6328: (66-80): CHC: Assertion violation happens here.\nCounterexample:\n\nx = true\ny = false\n\nTransaction trace:\nC.constructor()\nC.f(true, false) diff --git a/test/libsolidity/smtCheckerTests/userTypes/user_type_as_struct_member_1.sol b/test/libsolidity/smtCheckerTests/userTypes/user_type_as_struct_member_1.sol index 5cb307385b62..1a72cad3da71 100644 --- a/test/libsolidity/smtCheckerTests/userTypes/user_type_as_struct_member_1.sol +++ b/test/libsolidity/smtCheckerTests/userTypes/user_type_as_struct_member_1.sol @@ -23,4 +23,3 @@ contract C { ); } } -// ---- diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index e8206e48331c..bd51fe920e31 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -145,6 +145,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) "--model-checker-contracts=contract1.yul:A,contract2.yul:B", "--model-checker-div-mod-no-slacks", "--model-checker-engine=bmc", + "--model-checker-ext-calls=trusted", "--model-checker-invariants=contract,reentrancy", "--model-checker-show-unproved", "--model-checker-solvers=z3,smtlib2", @@ -211,6 +212,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) {{{"contract1.yul", {"A"}}, {"contract2.yul", {"B"}}}}, true, {true, false}, + {ModelCheckerExtCalls::Mode::TRUSTED}, {{InvariantType::Contract, InvariantType::Reentrancy}}, true, {false, false, true, true}, diff --git a/test/tools/fuzzer_common.cpp b/test/tools/fuzzer_common.cpp index 895957f4d2de..47ab7e49c262 100644 --- a/test/tools/fuzzer_common.cpp +++ b/test/tools/fuzzer_common.cpp @@ -108,6 +108,7 @@ void FuzzerUtil::testCompiler( frontend::ModelCheckerContracts::Default(), /*divModWithSlacks*/true, frontend::ModelCheckerEngine::All(), + frontend::ModelCheckerExtCalls{}, frontend::ModelCheckerInvariants::All(), /*showUnproved=*/false, smtutil::SMTSolverChoice::All(), From 411841cbb54283d5206b39e411e8a58d4bf404b5 Mon Sep 17 00:00:00 2001 From: Nuno Santos Date: Mon, 6 Feb 2023 17:19:48 +0000 Subject: [PATCH 023/228] Tweak wording in value type docs (#13935) * Update value-types.rst - User-defined were written inconsistently (with/without hyphen) - Rewording some sentences for user clarity. * Update docs/types/value-types.rst Co-authored-by: chriseth --------- Co-authored-by: chriseth --- docs/types/value-types.rst | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index de2724afa991..ffdec37961cf 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -4,8 +4,7 @@ Value Types =========== -The following types are also called value types because variables of these -types will always be passed by value, i.e. they are always copied when they +The following are called value types because their variables will always be passed by value, i.e. they are always copied when they are used as function arguments or in assignments. .. index:: ! bool, ! true, ! false @@ -47,7 +46,7 @@ access the minimum and maximum value representable by the type. Integers in Solidity are restricted to a certain range. For example, with ``uint32``, this is ``0`` up to ``2**32 - 1``. There are two modes in which arithmetic is performed on these types: The "wrapping" or "unchecked" mode and the "checked" mode. - By default, arithmetic is always "checked", which mean that if the result of an operation falls outside the value range + By default, arithmetic is always "checked", meaning that if an operation's result falls outside the value range of the type, the call is reverted through a :ref:`failing assertion`. You can switch to "unchecked" mode using ``unchecked { ... }``. More details can be found in the section about :ref:`unchecked `. @@ -182,7 +181,7 @@ Operators: Address ------- -The address type comes in two flavours, which are largely identical: +The address type comes in two largely identical flavors: - ``address``: Holds a 20 byte value (size of an Ethereum address). - ``address payable``: Same as ``address``, but with the additional members ``transfer`` and ``send``. @@ -653,13 +652,13 @@ smallest and respectively largest value of the given enum. .. _user-defined-value-types: -User Defined Value Types +User-defined Value Types ------------------------ -A user defined value type allows creating a zero cost abstraction over an elementary value type. +A user-defined value type allows creating a zero cost abstraction over an elementary value type. This is similar to an alias, but with stricter type requirements. -A user defined value type is defined using ``type C is V``, where ``C`` is the name of the newly +A user-defined value type is defined using ``type C is V``, where ``C`` is the name of the newly introduced type and ``V`` has to be a built-in value type (the "underlying type"). The function ``C.wrap`` is used to convert from the underlying type to the custom type. Similarly, the function ``C.unwrap`` is used to convert from the custom type to the underlying type. @@ -680,7 +679,7 @@ type with 18 decimals and a minimal library to do arithmetic operations on the t // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.8; - // Represent a 18 decimal, 256 bit wide fixed point type using a user defined value type. + // Represent a 18 decimal, 256 bit wide fixed point type using a user-defined value type. type UFixed256x18 is uint256; /// A minimal library to do fixed point operations on UFixed256x18. From 98c011328a1a6f46c4b466f235d1797bd3f57bf5 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Mon, 6 Feb 2023 16:53:23 -0300 Subject: [PATCH 024/228] Replace quote marks with quotation marks --- test/externalTests/gnosis.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/externalTests/gnosis.sh b/test/externalTests/gnosis.sh index ab1fbf3e1738..ed525e7a6502 100755 --- a/test/externalTests/gnosis.sh +++ b/test/externalTests/gnosis.sh @@ -72,9 +72,10 @@ function gnosis_safe_test # Disable tests that won't pass on the ir presets due to Hardhat heuristics. Note that this also disables # them for other presets but that's fine - we want same code run for benchmarks to be comparable. # TODO: Remove this when Hardhat adjusts heuristics for IR (https://github.com/nomiclabs/hardhat/issues/3365). - sed -i "s|\(it\)\(('should not allow to call setup on singleton'\)|\1.skip\2|g" test/core/Safe.Setup.spec.ts - sed -i "s|\(it\)\(('can be used only via DELEGATECALL opcode'\)|\1.skip\2|g" test/libraries/SignMessageLib.spec.ts - sed -i "s|it\(('can only be called from Safe itself'\)|it.skip\1|g" test/libraries/Migration.spec.ts + sed -i "s|\(it\)\((\"should not allow to call setup on singleton\"\)|\1.skip\2|g" test/core/Safe.Setup.spec.ts + sed -i "s|\(it\)\((\"can be used only via DELEGATECALL opcode\"\)|\1.skip\2|g" test/libraries/SignMessageLib.spec.ts + sed -i "s|it\((\"can only be called from Safe itself\"\)|it.skip\1|g" test/libraries/Migration.spec.ts + sed -i "s|\(it\)\((\"should revert if called directly\"\)|\1.skip\2|g" test/handlers/CompatibilityFallbackHandler.spec.ts neutralize_package_lock neutralize_package_json_hooks From a5c5e4602a51976ab59caa7282fa71ff59c3cb25 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Mon, 6 Feb 2023 18:04:10 -0300 Subject: [PATCH 025/228] Cleanup of duplicated line leftover from previous fix --- test/externalTests/gnosis.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/externalTests/gnosis.sh b/test/externalTests/gnosis.sh index ed525e7a6502..bafc3f2ca8c1 100755 --- a/test/externalTests/gnosis.sh +++ b/test/externalTests/gnosis.sh @@ -66,8 +66,8 @@ function gnosis_safe_test sed -i 's|"@openzeppelin/contracts": "\^3\.4\.0"|"@openzeppelin/contracts": "^4.0.0"|g' package.json # Disable two tests failing due to Hardhat's heuristics not yet updated to handle solc 0.8.10. - # TODO: Remove this when Hardhat implements them (https://github.com/nomiclabs/hardhat/issues/2051). - sed -i "s|\(it\)\(('should revert if called directly', async () => {\)|\1.skip\2|g" test/handlers/CompatibilityFallbackHandler.spec.ts + # TODO: Remove this when Hardhat implements them (https://github.com/nomiclabs/hardhat/issues/2451). + sed -i "s|\(it\)\((\"should revert if called directly\"\)|\1.skip\2|g" test/handlers/CompatibilityFallbackHandler.spec.ts # Disable tests that won't pass on the ir presets due to Hardhat heuristics. Note that this also disables # them for other presets but that's fine - we want same code run for benchmarks to be comparable. @@ -75,7 +75,6 @@ function gnosis_safe_test sed -i "s|\(it\)\((\"should not allow to call setup on singleton\"\)|\1.skip\2|g" test/core/Safe.Setup.spec.ts sed -i "s|\(it\)\((\"can be used only via DELEGATECALL opcode\"\)|\1.skip\2|g" test/libraries/SignMessageLib.spec.ts sed -i "s|it\((\"can only be called from Safe itself\"\)|it.skip\1|g" test/libraries/Migration.spec.ts - sed -i "s|\(it\)\((\"should revert if called directly\"\)|\1.skip\2|g" test/handlers/CompatibilityFallbackHandler.spec.ts neutralize_package_lock neutralize_package_json_hooks From db9c11a2a5e44771ca2bcd2f86ee2f6b7ea3abf5 Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Wed, 8 Feb 2023 16:55:14 +0100 Subject: [PATCH 026/228] fix abstract nondet exception --- Changelog.md | 1 + libsolidity/formal/SMTEncoder.cpp | 12 +++--------- libsolidity/formal/SMTEncoder.h | 3 --- .../natspec/abstract_free_function_1.sol | 17 +++++++++++++++++ 4 files changed, 21 insertions(+), 12 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/natspec/abstract_free_function_1.sol diff --git a/Changelog.md b/Changelog.md index 5ce4057a4800..eefabf7d6398 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Compiler Features: Bugfixes: + * SMTChecker: Fix internal error when using the custom NatSpec annotation to abstract free functions. * TypeChecker: Also allow external library functions in ``using for``. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index c3286fa041bc..5c4fc5ded0ae 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -2897,7 +2897,9 @@ vector SMTEncoder::stateVariablesIncludingInheritedA vector SMTEncoder::stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function) { - return stateVariablesIncludingInheritedAndPrivate(dynamic_cast(*_function.scope())); + if (auto contract = dynamic_cast(_function.scope())) + return stateVariablesIncludingInheritedAndPrivate(*contract); + return {}; } vector SMTEncoder::localVariablesIncludingModifiers(FunctionDefinition const& _function, ContractDefinition const* _contract) @@ -3020,14 +3022,6 @@ set const& SMTEncoder::contract return m_contractFunctionsWithoutVirtual.at(&_contract); } -SourceUnit const* SMTEncoder::sourceUnitContaining(Scopable const& _scopable) -{ - for (auto const* s = &_scopable; s; s = dynamic_cast(s->scope())) - if (auto const* source = dynamic_cast(s->scope())) - return source; - solAssert(false, ""); -} - map>> SMTEncoder::baseArguments(ContractDefinition const& _contract) { map>> baseArgs; diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index 06b03a7eb8a7..47e51b54de7e 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -113,9 +113,6 @@ class SMTEncoder: public ASTConstVisitor /// @returns the ModifierDefinition of a ModifierInvocation if possible, or nullptr. static ModifierDefinition const* resolveModifierInvocation(ModifierInvocation const& _invocation, ContractDefinition const* _contract); - /// @returns the SourceUnit that contains _scopable. - static SourceUnit const* sourceUnitContaining(Scopable const& _scopable); - /// @returns the arguments for each base constructor call in the hierarchy of @a _contract. std::map>> baseArguments(ContractDefinition const& _contract); diff --git a/test/libsolidity/smtCheckerTests/natspec/abstract_free_function_1.sol b/test/libsolidity/smtCheckerTests/natspec/abstract_free_function_1.sol new file mode 100644 index 000000000000..d013670594ed --- /dev/null +++ b/test/libsolidity/smtCheckerTests/natspec/abstract_free_function_1.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.13; + +contract C { + function f(uint x) external pure { + uint t = msb(x); + assert(t == 0); // should fail + } +} + +/// @custom:smtchecker abstract-function-nondet +function msb(uint256 x) pure returns (uint256 result) {} + +// ==== +// SMTEngine: chc +// ---- +// Warning 6328: (144-158): CHC: Assertion violation happens here. From 603f9f7208eacbbd1e07408b7f636d5b2ce0eea6 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Sun, 25 Dec 2022 22:43:26 +0100 Subject: [PATCH 027/228] Type recognition workaround for some GCC compilers Looks like somewhat old GCC compilers, namely 12.2.1, cannot recognize a string literal sometimes. Let's help it to avoid error logs like this one: ``` [ 75%] Building CXX object libsolidity/CMakeFiles/solidity.dir/codegen/ir/IRGeneratorForStatements.cpp.o cd /builddir/build/BUILD/solidity-0.8.18/redhat-linux-build/libsolidity && /usr/bin/g++ -DBOOST_ATOMIC_DYN_LINK -DBOOST_ATOMIC_NO_LIB -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_FILESYSTEM_NO_LIB -DBOOST_SYSTEM_DYN_LINK -DBOOST_SYSTEM_NO_LIB -DFMT_HEADER_ONLY=1 -DHAVE_CVC4 -DHAVE_Z3 -I/builddir/build/BUILD/solidity-0.8.18/redhat-linux-build/include -I/builddir/build/BUILD/solidity-0.8.18 -isystem /usr/include/z3 -O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fuse-ld=gold -O3 -DNDEBUG -fstack-protector-strong -Wimplicit-fallthrough -fmacro-prefix-map=/builddir/build/BUILD/solidity-0.8.18=/solidity -Wpessimizing-move -Wredundant-move -Wall -Wextra -Werror -pedantic -Wmissing-declarations -Wno-unknown-pragmas -Wsign-conversion -Wconversion -Wextra-semi -Wduplicated-cond -Wlogical-op -fdiagnostics-color -std=c++17 -MD -MT libsolidity/CMakeFiles/solidity.dir/codegen/ir/IRGeneratorForStatements.cpp.o -MF CMakeFiles/solidity.dir/codegen/ir/IRGeneratorForStatements.cpp.o.d -o CMakeFiles/solidity.dir/codegen/ir/IRGeneratorForStatements.cpp.o -c /builddir/build/BUILD/solidity-0.8.18/libsolidity/codegen/ir/IRGeneratorForStatements.cpp In file included from /usr/include/c++/12/string:40, from /builddir/build/BUILD/solidity-0.8.18/libsolidity/ast/ASTForward.h:27, from /builddir/build/BUILD/solidity-0.8.18/libsolidity/ast/AST.h:26, from /builddir/build/BUILD/solidity-0.8.18/libsolidity/ast/ASTVisitor.h:26, from /builddir/build/BUILD/solidity-0.8.18/libsolidity/codegen/ir/IRGeneratorForStatements.h:24, from /builddir/build/BUILD/solidity-0.8.18/libsolidity/codegen/ir/IRGeneratorForStatements.cpp:22: In function 'std::char_traits::copy(char*, char const*, unsigned long)', inlined from 'std::__cxx11::basic_string, std::allocator >::_S_copy(char*, char const*, unsigned long)' at /usr/include/c++/12/bits/basic_string.h:423:21, inlined from 'std::__cxx11::basic_string, std::allocator >::_M_replace(unsigned long, unsigned long, char const*, unsigned long)' at /usr/include/c++/12/bits/basic_string.tcc:532:22, inlined from 'std::__cxx11::basic_string, std::allocator >::assign(char const*)' at /usr/include/c++/12/bits/basic_string.h:1647:19, inlined from 'std::__cxx11::basic_string, std::allocator >::operator=(char const*)' at /usr/include/c++/12/bits/basic_string.h:815:28, inlined from '(anonymous namespace)::CopyTranslate::translateReference(solidity::yul::Identifier const&)' at /builddir/build/BUILD/solidity-0.8.18/libsolidity/codegen/ir/IRGeneratorForStatements.cpp:182:13: /usr/include/c++/12/bits/char_traits.h:431:56: error: 'memcpy' accessing 9223372036854775810 or more bytes at offsets -4611686018427387902 and [-4611686018427387903, 4611686018427387904] may overlap up to 9223372036854775813 bytes at offset -3 [-Werror=restrict] 431 | return static_cast(__builtin_memcpy(__s1, __s2, __n)); | ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~ cc1plus: all warnings being treated as errors gmake[2]: *** [libsolidity/CMakeFiles/solidity.dir/build.make:695: libsolidity/CMakeFiles/solidity.dir/codegen/ir/IRGeneratorForStatements.cpp.o] Error 1 gmake[2]: *** Waiting for unfinished jobs.... gmake[2]: Leaving directory '/builddir/build/BUILD/solidity-0.8.18/redhat-linux-build' gmake[1]: *** [CMakeFiles/Makefile2:414: libsolidity/CMakeFiles/solidity.dir/all] Error 2 gmake[1]: Leaving directory '/builddir/build/BUILD/solidity-0.8.18/redhat-linux-build' gmake: *** [Makefile:139: all] Error 2 ``` Signed-off-by: Peter Lemenkov --- libsolidity/ast/ASTJsonExporter.cpp | 3 ++- libsolidity/codegen/ir/IRGeneratorForStatements.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libsolidity/ast/ASTJsonExporter.cpp b/libsolidity/ast/ASTJsonExporter.cpp index 8b46684bc4fd..7db502eefbc1 100644 --- a/libsolidity/ast/ASTJsonExporter.cpp +++ b/libsolidity/ast/ASTJsonExporter.cpp @@ -45,6 +45,7 @@ #include using namespace std; +using namespace std::string_literals; using namespace solidity::langutil; namespace @@ -138,7 +139,7 @@ Json::Value ASTJsonExporter::sourceLocationsToJson(vector const& string ASTJsonExporter::namePathToString(std::vector const& _namePath) { - return boost::algorithm::join(_namePath, "."); + return boost::algorithm::join(_namePath, "."s); } Json::Value ASTJsonExporter::typePointerToJson(Type const* _tp, bool _withoutDataLocation) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 3e3041bc108d..fd0199204bd6 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -179,7 +179,7 @@ struct CopyTranslate: public yul::ASTCopier else { solAssert(!IRVariable{*varDecl}.hasPart("offset")); - value = "0"; + value = "0"s; } } else if (varDecl->type()->dataStoredIn(DataLocation::CallData)) From a38549dc190b06bbc856734788563907c885ebeb Mon Sep 17 00:00:00 2001 From: Pawel Gebal Date: Fri, 3 Feb 2023 11:29:58 +0100 Subject: [PATCH 028/228] Fixes handling bitwise operators for z3 model checker --- Changelog.md | 1 + libsmtutil/Z3Interface.cpp | 6 ++++++ .../bitwise_operators_do_not_throw_exceptions.sol | 12 ++++++++++++ 3 files changed, 19 insertions(+) create mode 100644 test/libsolidity/smtCheckerTests/operators/bitwise_operators_do_not_throw_exceptions.sol diff --git a/Changelog.md b/Changelog.md index 7619fbd40db2..bff045843d9a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Compiler Features: Bugfixes: * TypeChecker: Also allow external library functions in ``using for``. + * SMTChecker: Fix internal error caused by unhandled ``z3`` expressions that come from the solver when bitwise operators are used. ### 0.8.18 (2023-02-01) diff --git a/libsmtutil/Z3Interface.cpp b/libsmtutil/Z3Interface.cpp index 4abeffa1694a..21b31d19f8de 100644 --- a/libsmtutil/Z3Interface.cpp +++ b/libsmtutil/Z3Interface.cpp @@ -344,6 +344,12 @@ Expression Z3Interface::fromZ3Expr(z3::expr const& _expr) return arguments[0] % arguments[1]; else if (kind == Z3_OP_XOR) return arguments[0] ^ arguments[1]; + else if (kind == Z3_OP_BOR) + return arguments[0] | arguments[1]; + else if (kind == Z3_OP_BAND) + return arguments[0] & arguments[1]; + else if (kind == Z3_OP_BXOR) + return arguments[0] ^ arguments[1]; else if (kind == Z3_OP_BNOT) return !arguments[0]; else if (kind == Z3_OP_BSHL) diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_operators_do_not_throw_exceptions.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_operators_do_not_throw_exceptions.sol new file mode 100644 index 000000000000..267705a0eba6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_operators_do_not_throw_exceptions.sol @@ -0,0 +1,12 @@ +contract C { + // tests that bitwise operators are parsed from z3 answer + function test(uint x, uint y) public pure { + x | y; + x & y; + x ^ y; + assert(true); + } +} +// ==== +// SMTEngine: all +// ---- From 47aa1c65ae7f3acc576372ec327f346d790bccea Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 10 Nov 2022 11:49:40 +0100 Subject: [PATCH 029/228] Re-implement KnowledgeBase using groups of constantly-spaced variables. --- libyul/optimiser/DataFlowAnalyzer.cpp | 4 +- libyul/optimiser/KnowledgeBase.cpp | 144 +++++++++++++++------ libyul/optimiser/KnowledgeBase.h | 57 ++++++-- libyul/optimiser/UnusedStoreEliminator.cpp | 8 +- test/libyul/KnowledgeBaseTest.cpp | 8 +- 5 files changed, 165 insertions(+), 56 deletions(-) diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index 59ba7c8f0e11..81028e30078c 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -50,7 +50,7 @@ DataFlowAnalyzer::DataFlowAnalyzer( ): m_dialect(_dialect), m_functionSideEffects(std::move(_functionSideEffects)), - m_knowledgeBase(_dialect, [this](YulString _var) { return variableValue(_var); }), + m_knowledgeBase([this](YulString _var) { return variableValue(_var); }), m_analyzeStores(_analyzeStores == MemoryAndStorage::Analyze) { if (m_analyzeStores) @@ -76,7 +76,7 @@ void DataFlowAnalyzer::operator()(ExpressionStatement& _statement) cxx20::erase_if(m_state.environment.storage, mapTuple([&](auto&& key, auto&& value) { return !m_knowledgeBase.knownToBeDifferent(vars->first, key) && - !m_knowledgeBase.knownToBeEqual(vars->second, value); + vars->second != value; })); m_state.environment.storage[vars->first] = vars->second; return; diff --git a/libyul/optimiser/KnowledgeBase.cpp b/libyul/optimiser/KnowledgeBase.cpp index c706d529b646..2fd5fb0a9bab 100644 --- a/libyul/optimiser/KnowledgeBase.cpp +++ b/libyul/optimiser/KnowledgeBase.cpp @@ -23,7 +23,6 @@ #include #include -#include #include #include @@ -36,37 +35,24 @@ using namespace solidity::yul; bool KnowledgeBase::knownToBeDifferent(YulString _a, YulString _b) { - // Try to use the simplification rules together with the - // current values to turn `sub(_a, _b)` into a nonzero constant. - // If that fails, try `eq(_a, _b)`. - if (optional difference = differenceIfKnownConstant(_a, _b)) return difference != 0; - - Expression expr2 = simplify(FunctionCall{{}, {{}, "eq"_yulstring}, util::make_vector(Identifier{{}, _a}, Identifier{{}, _b})}); - if (holds_alternative(expr2)) - return valueOfLiteral(std::get(expr2)) == 0; - return false; } optional KnowledgeBase::differenceIfKnownConstant(YulString _a, YulString _b) { - // Try to use the simplification rules together with the - // current values to turn `sub(_a, _b)` into a constant. - - Expression expr1 = simplify(FunctionCall{{}, {{}, "sub"_yulstring}, util::make_vector(Identifier{{}, _a}, Identifier{{}, _b})}); - if (Literal const* value = get_if(&expr1)) - return valueOfLiteral(*value); - - return {}; + VariableOffset offA = explore(_a); + VariableOffset offB = explore(_b); + if (offA.reference == offB.reference) + return offA.offset - offB.offset; + else + return {}; } + bool KnowledgeBase::knownToBeDifferentByAtLeast32(YulString _a, YulString _b) { - // Try to use the simplification rules together with the - // current values to turn `sub(_a, _b)` into a constant whose absolute value is at least 32. - if (optional difference = differenceIfKnownConstant(_a, _b)) return difference >= 32 && difference <= u256(0) - 32; @@ -80,29 +66,113 @@ bool KnowledgeBase::knownToBeZero(YulString _a) optional KnowledgeBase::valueIfKnownConstant(YulString _a) { - if (AssignedValue const* value = m_variableValues(_a)) - if (Literal const* literal = get_if(value->value)) - return valueOfLiteral(*literal); - return {}; + VariableOffset offset = explore(_a); + if (offset.reference == YulString{}) + return offset.offset; + else + return nullopt; +} + +optional KnowledgeBase::valueIfKnownConstant(Expression const& _expression) +{ + if (Identifier const* ident = get_if(&_expression)) + return valueIfKnownConstant(ident->name); + else if (Literal const* lit = get_if(&_expression)) + return valueOfLiteral(*lit); + else + return {}; } -Expression KnowledgeBase::simplify(Expression _expression) +KnowledgeBase::VariableOffset KnowledgeBase::explore(YulString _var) { - m_counter = 0; - return simplifyRecursively(std::move(_expression)); + // We query the value first so that the variable is reset if it has changed + // since the last call. + Expression const* value = valueOf(_var); + if (VariableOffset const* varOff = util::valueOrNullptr(m_offsets, _var)) + return *varOff; + + if (value) + if (optional offset = explore(*value)) + return setOffset(_var, *offset); + return setOffset(_var, VariableOffset{_var, 0}); + } -Expression KnowledgeBase::simplifyRecursively(Expression _expression) +optional KnowledgeBase::explore(Expression const& _value) { - if (m_counter++ > 100) - return _expression; + if (Literal const* literal = std::get_if(&_value)) + return VariableOffset{YulString{}, valueOfLiteral(*literal)}; + else if (Identifier const* identifier = std::get_if(&_value)) + return explore(identifier->name); + else if (FunctionCall const* f = get_if(&_value)) + if (f->functionName.name == "add"_yulstring || f->functionName.name == "sub"_yulstring) + if (optional a = explore(f->arguments[0])) + if (optional b = explore(f->arguments[1])) + { + u256 offset = + f->functionName.name == "add"_yulstring ? + a->offset + b->offset : + a->offset - b->offset; + if (a->reference == b->reference) + // Offsets relative to the same reference variable + return VariableOffset{a->reference, offset}; + else if (a->reference == YulString{}) + // a is constant + return VariableOffset{b->reference, offset}; + else if (b->reference == YulString{}) + // b is constant + return VariableOffset{a->reference, offset}; + } - if (holds_alternative(_expression)) - for (Expression& arg: std::get(_expression).arguments) - arg = simplifyRecursively(arg); + return {}; +} - if (auto match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_variableValues)) - return simplifyRecursively(match->action().toExpression(debugDataOf(_expression), langutil::EVMVersion())); +Expression const* KnowledgeBase::valueOf(YulString _var) +{ + Expression const* lastValue = m_lastKnownValue[_var]; + AssignedValue const* assignedValue = m_variableValues(_var); + Expression const* currentValue = assignedValue ? assignedValue->value : nullptr; + if (lastValue != currentValue) + reset(_var); + m_lastKnownValue[_var] = currentValue; + return currentValue; +} - return _expression; +void KnowledgeBase::reset(YulString _var) +{ + m_lastKnownValue.erase(_var); + if (VariableOffset const* offset = util::valueOrNullptr(m_offsets, _var)) + { + // Remove var from its group + if (offset->reference != YulString{}) + m_groupMembers[offset->reference].erase(_var); + m_offsets.erase(_var); + } + if (set* group = util::valueOrNullptr(m_groupMembers, _var)) + { + // _var was a representative, we might have to find a new one. + if (group->empty()) + m_groupMembers.erase(_var); + else + { + YulString newRepresentative = *group->begin(); + u256 newOffset = m_offsets[newRepresentative].offset; + for (YulString groupMember: *group) + { + yulAssert(m_offsets[groupMember].reference == _var); + m_offsets[groupMember].reference = newRepresentative; + m_offsets[newRepresentative].offset -= newOffset; + } + } + } +} + +KnowledgeBase::VariableOffset KnowledgeBase::setOffset(YulString _variable, VariableOffset _value) +{ + m_offsets[_variable] = _value; + // Constants are not tracked in m_groupMembers because + // the "representative" can never be reset. + if (_value.reference != YulString{}) + m_groupMembers[_value.reference].insert(_variable); + return _value; } diff --git a/libyul/optimiser/KnowledgeBase.h b/libyul/optimiser/KnowledgeBase.h index 999d0e312bc4..82c82a7e93ee 100644 --- a/libyul/optimiser/KnowledgeBase.h +++ b/libyul/optimiser/KnowledgeBase.h @@ -38,32 +38,69 @@ struct AssignedValue; /** * Class that can answer questions about values of variables and their relations. + * + * Requires a callback that returns the current value of the variable. + * The value can change any time during the lifetime of the KnowledgeBase, + * it will update its internal data structure accordingly. + * + * This means that the code the KnowledgeBase is used on does not need to be in SSA + * form. + * The only requirement is that the assigned values are movable expressions. + * + * Internally, tries to find groups of variables that have a mutual constant + * difference and stores these differences always relative to a specific + * representative variable of the group. + * + * There is a special group which is the constant values. Those use the + * empty YulString as representative "variable". */ class KnowledgeBase { public: - KnowledgeBase( - Dialect const& _dialect, - std::function _variableValues - ): - m_dialect(_dialect), + KnowledgeBase(std::function _variableValues): m_variableValues(std::move(_variableValues)) {} bool knownToBeDifferent(YulString _a, YulString _b); std::optional differenceIfKnownConstant(YulString _a, YulString _b); bool knownToBeDifferentByAtLeast32(YulString _a, YulString _b); - bool knownToBeEqual(YulString _a, YulString _b) const { return _a == _b; } bool knownToBeZero(YulString _a); std::optional valueIfKnownConstant(YulString _a); + std::optional valueIfKnownConstant(Expression const& _expression); private: - Expression simplify(Expression _expression); - Expression simplifyRecursively(Expression _expression); + /** + * Constant offset relative to a reference variable, or absolute constant if the + * reference variable is the empty YulString. + */ + struct VariableOffset + { + YulString reference; + u256 offset; + }; - Dialect const& m_dialect; + VariableOffset explore(YulString _var); + std::optional explore(Expression const& _value); + + /// Retrieves the current value of a variable and potentially resets the variable if it is not up to date. + Expression const* valueOf(YulString _var); + + /// Resets all information about the variable and removes it from its group, + /// potentially finding a new representative. + void reset(YulString _var); + + VariableOffset setOffset(YulString _variable, VariableOffset _value); + + /// Callback to retrieve the current value of a variable. std::function m_variableValues; - size_t m_counter = 0; + + /// Offsets for each variable to one representative per group. + /// The empty string is the representative of the constant value zero. + std::map m_offsets; + /// Last known value of each variable we queried. + std::map m_lastKnownValue; + /// For each representative, variables that use it to offset from. + std::map> m_groupMembers; }; } diff --git a/libyul/optimiser/UnusedStoreEliminator.cpp b/libyul/optimiser/UnusedStoreEliminator.cpp index 2e96be2e703a..d5155ad7e55b 100644 --- a/libyul/optimiser/UnusedStoreEliminator.cpp +++ b/libyul/optimiser/UnusedStoreEliminator.cpp @@ -174,7 +174,7 @@ void UnusedStoreEliminator::visit(Statement const& _statement) initialState = State::Used; auto startOffset = identifierNameIfSSA(funCall->arguments.at(1)); auto length = identifierNameIfSSA(funCall->arguments.at(2)); - KnowledgeBase knowledge(m_dialect, [this](YulString _var) { return util::valueOrNullptr(m_ssaValues, _var); }); + KnowledgeBase knowledge([this](YulString _var) { return util::valueOrNullptr(m_ssaValues, _var); }); if (length && startOffset) { FunctionCall const* lengthCall = get_if(m_ssaValues.at(*length).value); @@ -267,7 +267,7 @@ bool UnusedStoreEliminator::knownUnrelated( UnusedStoreEliminator::Operation const& _op2 ) const { - KnowledgeBase knowledge(m_dialect, [this](YulString _var) { return util::valueOrNullptr(m_ssaValues, _var); }); + KnowledgeBase knowledge([this](YulString _var) { return util::valueOrNullptr(m_ssaValues, _var); }); if (_op1.location != _op2.location) return true; @@ -348,7 +348,7 @@ bool UnusedStoreEliminator::knownCovered( return true; if (_covered.location == Location::Memory) { - KnowledgeBase knowledge(m_dialect, [this](YulString _var) { return util::valueOrNullptr(m_ssaValues, _var); }); + KnowledgeBase knowledge([this](YulString _var) { return util::valueOrNullptr(m_ssaValues, _var); }); if (_covered.length && knowledge.knownToBeZero(*_covered.length)) return true; @@ -359,7 +359,7 @@ bool UnusedStoreEliminator::knownCovered( return false; optional coveredLength = knowledge.valueIfKnownConstant(*_covered.length); optional coveringLength = knowledge.valueIfKnownConstant(*_covering.length); - if (knowledge.knownToBeEqual(*_covered.start, *_covering.start)) + if (*_covered.start == *_covering.start) if (coveredLength && coveringLength && *coveredLength <= *coveringLength) return true; optional coveredStart = knowledge.valueIfKnownConstant(*_covered.start); diff --git a/test/libyul/KnowledgeBaseTest.cpp b/test/libyul/KnowledgeBaseTest.cpp index ec2f0313d18e..7cb5a8ae3c05 100644 --- a/test/libyul/KnowledgeBaseTest.cpp +++ b/test/libyul/KnowledgeBaseTest.cpp @@ -58,7 +58,7 @@ class KnowledgeBaseTest for (auto const& [name, expression]: m_ssaValues.values()) m_values[name].value = expression; - return KnowledgeBase(m_dialect, [this](YulString _var) { return util::valueOrNullptr(m_values, _var); }); + return KnowledgeBase([this](YulString _var) { return util::valueOrNullptr(m_values, _var); }); } EVMDialect m_dialect{EVMVersion{}, true}; @@ -83,9 +83,11 @@ BOOST_AUTO_TEST_CASE(basic) BOOST_CHECK(!kb.knownToBeDifferent("a"_yulstring, "b"_yulstring)); // This only works if the variable names are the same. // It assumes that SSA+CSE+Simplifier actually replaces the variables. - BOOST_CHECK(!kb.knownToBeEqual("a"_yulstring, "b"_yulstring)); BOOST_CHECK(!kb.valueIfKnownConstant("a"_yulstring)); BOOST_CHECK(kb.valueIfKnownConstant("zero"_yulstring) == u256(0)); + BOOST_CHECK(kb.differenceIfKnownConstant("a"_yulstring, "b"_yulstring) == u256(0)); + BOOST_CHECK(kb.differenceIfKnownConstant("a"_yulstring, "c"_yulstring) == u256(0)); + BOOST_CHECK(kb.valueIfKnownConstant("e"_yulstring) == u256(0)); } BOOST_AUTO_TEST_CASE(difference) @@ -94,7 +96,7 @@ BOOST_AUTO_TEST_CASE(difference) let a := calldataload(0) let b := add(a, 200) let c := add(a, 220) - let d := add(c, 12) + let d := add(12, c) let e := sub(c, 12) })"); From 96e2a6d3fe7eb4b7fc197f30910411afbcb869a1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 10 Nov 2022 14:06:37 +0100 Subject: [PATCH 030/228] Keep one instance of KnowledgeBase for UnusedStoreEliminator. --- libyul/optimiser/UnusedStoreEliminator.cpp | 60 +++++++++++++--------- libyul/optimiser/UnusedStoreEliminator.h | 11 ++-- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/libyul/optimiser/UnusedStoreEliminator.cpp b/libyul/optimiser/UnusedStoreEliminator.cpp index d5155ad7e55b..24553339526b 100644 --- a/libyul/optimiser/UnusedStoreEliminator.cpp +++ b/libyul/optimiser/UnusedStoreEliminator.cpp @@ -92,6 +92,21 @@ void UnusedStoreEliminator::run(OptimiserStepContext& _context, Block& _ast) remover(_ast); } +UnusedStoreEliminator::UnusedStoreEliminator( + Dialect const& _dialect, + map const& _functionSideEffects, + map _controlFlowSideEffects, + map const& _ssaValues, + bool _ignoreMemory +): + UnusedStoreBase(_dialect), + m_ignoreMemory(_ignoreMemory), + m_functionSideEffects(_functionSideEffects), + m_controlFlowSideEffects(_controlFlowSideEffects), + m_ssaValues(_ssaValues), + m_knowledgeBase([this](YulString _var) { return util::valueOrNullptr(m_ssaValues, _var); }) +{} + void UnusedStoreEliminator::operator()(FunctionCall const& _functionCall) { UnusedStoreBase::operator()(_functionCall); @@ -174,12 +189,11 @@ void UnusedStoreEliminator::visit(Statement const& _statement) initialState = State::Used; auto startOffset = identifierNameIfSSA(funCall->arguments.at(1)); auto length = identifierNameIfSSA(funCall->arguments.at(2)); - KnowledgeBase knowledge([this](YulString _var) { return util::valueOrNullptr(m_ssaValues, _var); }); if (length && startOffset) { FunctionCall const* lengthCall = get_if(m_ssaValues.at(*length).value); if ( - knowledge.knownToBeZero(*startOffset) && + m_knowledgeBase.knownToBeZero(*startOffset) && lengthCall && toEVMInstruction(m_dialect, lengthCall->functionName.name) == Instruction::RETURNDATASIZE ) @@ -267,8 +281,6 @@ bool UnusedStoreEliminator::knownUnrelated( UnusedStoreEliminator::Operation const& _op2 ) const { - KnowledgeBase knowledge([this](YulString _var) { return util::valueOrNullptr(m_ssaValues, _var); }); - if (_op1.location != _op2.location) return true; if (_op1.location == Location::Storage) @@ -278,26 +290,26 @@ bool UnusedStoreEliminator::knownUnrelated( yulAssert( _op1.length && _op2.length && - knowledge.valueIfKnownConstant(*_op1.length) == 1 && - knowledge.valueIfKnownConstant(*_op2.length) == 1 + m_knowledgeBase.valueIfKnownConstant(*_op1.length) == 1 && + m_knowledgeBase.valueIfKnownConstant(*_op2.length) == 1 ); - return knowledge.knownToBeDifferent(*_op1.start, *_op2.start); + return m_knowledgeBase.knownToBeDifferent(*_op1.start, *_op2.start); } } else { yulAssert(_op1.location == Location::Memory, ""); if ( - (_op1.length && knowledge.knownToBeZero(*_op1.length)) || - (_op2.length && knowledge.knownToBeZero(*_op2.length)) + (_op1.length && m_knowledgeBase.knownToBeZero(*_op1.length)) || + (_op2.length && m_knowledgeBase.knownToBeZero(*_op2.length)) ) return true; if (_op1.start && _op1.length && _op2.start) { - optional length1 = knowledge.valueIfKnownConstant(*_op1.length); - optional start1 = knowledge.valueIfKnownConstant(*_op1.start); - optional start2 = knowledge.valueIfKnownConstant(*_op2.start); + optional length1 = m_knowledgeBase.valueIfKnownConstant(*_op1.length); + optional start1 = m_knowledgeBase.valueIfKnownConstant(*_op1.start); + optional start2 = m_knowledgeBase.valueIfKnownConstant(*_op2.start); if ( (length1 && start1 && start2) && *start1 + *length1 >= *start1 && // no overflow @@ -307,9 +319,9 @@ bool UnusedStoreEliminator::knownUnrelated( } if (_op2.start && _op2.length && _op1.start) { - optional length2 = knowledge.valueIfKnownConstant(*_op2.length); - optional start2 = knowledge.valueIfKnownConstant(*_op2.start); - optional start1 = knowledge.valueIfKnownConstant(*_op1.start); + optional length2 = m_knowledgeBase.valueIfKnownConstant(*_op2.length); + optional start2 = m_knowledgeBase.valueIfKnownConstant(*_op2.start); + optional start1 = m_knowledgeBase.valueIfKnownConstant(*_op1.start); if ( (length2 && start2 && start1) && *start2 + *length2 >= *start2 && // no overflow @@ -320,12 +332,12 @@ bool UnusedStoreEliminator::knownUnrelated( if (_op1.start && _op1.length && _op2.start && _op2.length) { - optional length1 = knowledge.valueIfKnownConstant(*_op1.length); - optional length2 = knowledge.valueIfKnownConstant(*_op2.length); + optional length1 = m_knowledgeBase.valueIfKnownConstant(*_op1.length); + optional length2 = m_knowledgeBase.valueIfKnownConstant(*_op2.length); if ( (length1 && *length1 <= 32) && (length2 && *length2 <= 32) && - knowledge.knownToBeDifferentByAtLeast32(*_op1.start, *_op2.start) + m_knowledgeBase.knownToBeDifferentByAtLeast32(*_op1.start, *_op2.start) ) return true; } @@ -348,22 +360,20 @@ bool UnusedStoreEliminator::knownCovered( return true; if (_covered.location == Location::Memory) { - KnowledgeBase knowledge([this](YulString _var) { return util::valueOrNullptr(m_ssaValues, _var); }); - - if (_covered.length && knowledge.knownToBeZero(*_covered.length)) + if (_covered.length && m_knowledgeBase.knownToBeZero(*_covered.length)) return true; // Condition (i = cover_i_ng, e = cover_e_d): // i.start <= e.start && e.start + e.length <= i.start + i.length if (!_covered.start || !_covering.start || !_covered.length || !_covering.length) return false; - optional coveredLength = knowledge.valueIfKnownConstant(*_covered.length); - optional coveringLength = knowledge.valueIfKnownConstant(*_covering.length); + optional coveredLength = m_knowledgeBase.valueIfKnownConstant(*_covered.length); + optional coveringLength = m_knowledgeBase.valueIfKnownConstant(*_covering.length); if (*_covered.start == *_covering.start) if (coveredLength && coveringLength && *coveredLength <= *coveringLength) return true; - optional coveredStart = knowledge.valueIfKnownConstant(*_covered.start); - optional coveringStart = knowledge.valueIfKnownConstant(*_covering.start); + optional coveredStart = m_knowledgeBase.valueIfKnownConstant(*_covered.start); + optional coveringStart = m_knowledgeBase.valueIfKnownConstant(*_covering.start); if (coveredStart && coveringStart && coveredLength && coveringLength) if ( *coveringStart <= *coveredStart && diff --git a/libyul/optimiser/UnusedStoreEliminator.h b/libyul/optimiser/UnusedStoreEliminator.h index 8b5bfd7b140b..7fbf9885ff3c 100644 --- a/libyul/optimiser/UnusedStoreEliminator.h +++ b/libyul/optimiser/UnusedStoreEliminator.h @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -68,13 +69,7 @@ class UnusedStoreEliminator: public UnusedStoreBase std::map _controlFlowSideEffects, std::map const& _ssaValues, bool _ignoreMemory - ): - UnusedStoreBase(_dialect), - m_ignoreMemory(_ignoreMemory), - m_functionSideEffects(_functionSideEffects), - m_controlFlowSideEffects(_controlFlowSideEffects), - m_ssaValues(_ssaValues) - {} + ); using UnusedStoreBase::operator(); void operator()(FunctionCall const& _functionCall) override; @@ -121,6 +116,8 @@ class UnusedStoreEliminator: public UnusedStoreBase std::map const& m_ssaValues; std::map m_storeOperations; + + KnowledgeBase mutable m_knowledgeBase; }; } From 6bbef64034be733f987514a065a1b05b1b3aae65 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 10 Nov 2022 14:22:40 +0100 Subject: [PATCH 031/228] Optimize in case this is SSA. --- libyul/optimiser/KnowledgeBase.cpp | 34 ++++++++++++++++++---- libyul/optimiser/KnowledgeBase.h | 9 ++++++ libyul/optimiser/UnusedStoreEliminator.cpp | 2 +- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/libyul/optimiser/KnowledgeBase.cpp b/libyul/optimiser/KnowledgeBase.cpp index 2fd5fb0a9bab..23627653a5b1 100644 --- a/libyul/optimiser/KnowledgeBase.cpp +++ b/libyul/optimiser/KnowledgeBase.cpp @@ -33,6 +33,11 @@ using namespace std; using namespace solidity; using namespace solidity::yul; +KnowledgeBase::KnowledgeBase(map const& _ssaValues): + m_valuesAreSSA(true), + m_variableValues([_ssaValues](YulString _var) { return util::valueOrNullptr(_ssaValues, _var); }) +{} + bool KnowledgeBase::knownToBeDifferent(YulString _a, YulString _b) { if (optional difference = differenceIfKnownConstant(_a, _b)) @@ -85,11 +90,23 @@ optional KnowledgeBase::valueIfKnownConstant(Expression const& _expression KnowledgeBase::VariableOffset KnowledgeBase::explore(YulString _var) { - // We query the value first so that the variable is reset if it has changed - // since the last call. - Expression const* value = valueOf(_var); - if (VariableOffset const* varOff = util::valueOrNullptr(m_offsets, _var)) - return *varOff; + Expression const* value = nullptr; + if (m_valuesAreSSA) + { + // In SSA, a once determined offset is always valid, so we first see + // if we already computed it. + if (VariableOffset const* varOff = util::valueOrNullptr(m_offsets, _var)) + return *varOff; + value = valueOf(_var); + } + else + { + // For non-SSA, we query the value first so that the variable is reset if it has changed + // since the last call. + value = valueOf(_var); + if (VariableOffset const* varOff = util::valueOrNullptr(m_offsets, _var)) + return *varOff; + } if (value) if (optional offset = explore(*value)) @@ -129,9 +146,12 @@ optional KnowledgeBase::explore(Expression const& Expression const* KnowledgeBase::valueOf(YulString _var) { - Expression const* lastValue = m_lastKnownValue[_var]; AssignedValue const* assignedValue = m_variableValues(_var); Expression const* currentValue = assignedValue ? assignedValue->value : nullptr; + if (m_valuesAreSSA) + return currentValue; + + Expression const* lastValue = m_lastKnownValue[_var]; if (lastValue != currentValue) reset(_var); m_lastKnownValue[_var] = currentValue; @@ -140,6 +160,8 @@ Expression const* KnowledgeBase::valueOf(YulString _var) void KnowledgeBase::reset(YulString _var) { + yulAssert(!m_valuesAreSSA); + m_lastKnownValue.erase(_var); if (VariableOffset const* offset = util::valueOrNullptr(m_offsets, _var)) { diff --git a/libyul/optimiser/KnowledgeBase.h b/libyul/optimiser/KnowledgeBase.h index 82c82a7e93ee..934b2e21ad94 100644 --- a/libyul/optimiser/KnowledgeBase.h +++ b/libyul/optimiser/KnowledgeBase.h @@ -47,6 +47,9 @@ struct AssignedValue; * form. * The only requirement is that the assigned values are movable expressions. * + * There is a constructor to provide all SSA values right at the beginning. + * If you use this, the KnowledgeBase will be slightly more efficient. + * * Internally, tries to find groups of variables that have a mutual constant * difference and stores these differences always relative to a specific * representative variable of the group. @@ -57,9 +60,13 @@ struct AssignedValue; class KnowledgeBase { public: + /// Constructor for arbitrary value callback that allows for variable values + /// to change in between calls to functions of this class. KnowledgeBase(std::function _variableValues): m_variableValues(std::move(_variableValues)) {} + /// Constructor to use if source code is in SSA form and values are constant. + KnowledgeBase(std::map const& _ssaValues); bool knownToBeDifferent(YulString _a, YulString _b); std::optional differenceIfKnownConstant(YulString _a, YulString _b); @@ -91,6 +98,8 @@ class KnowledgeBase VariableOffset setOffset(YulString _variable, VariableOffset _value); + /// If true, we can assume that variable values never change and skip some steps. + bool m_valuesAreSSA = false; /// Callback to retrieve the current value of a variable. std::function m_variableValues; diff --git a/libyul/optimiser/UnusedStoreEliminator.cpp b/libyul/optimiser/UnusedStoreEliminator.cpp index 24553339526b..3df9c6319638 100644 --- a/libyul/optimiser/UnusedStoreEliminator.cpp +++ b/libyul/optimiser/UnusedStoreEliminator.cpp @@ -104,7 +104,7 @@ UnusedStoreEliminator::UnusedStoreEliminator( m_functionSideEffects(_functionSideEffects), m_controlFlowSideEffects(_controlFlowSideEffects), m_ssaValues(_ssaValues), - m_knowledgeBase([this](YulString _var) { return util::valueOrNullptr(m_ssaValues, _var); }) + m_knowledgeBase(_ssaValues) {} void UnusedStoreEliminator::operator()(FunctionCall const& _functionCall) From eec258c2d29d0b36635ad2e398603d6342176842 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 10 Nov 2022 16:04:19 +0100 Subject: [PATCH 032/228] Bugfix. --- libyul/optimiser/KnowledgeBase.cpp | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/libyul/optimiser/KnowledgeBase.cpp b/libyul/optimiser/KnowledgeBase.cpp index 23627653a5b1..558dc8988800 100644 --- a/libyul/optimiser/KnowledgeBase.cpp +++ b/libyul/optimiser/KnowledgeBase.cpp @@ -122,24 +122,33 @@ optional KnowledgeBase::explore(Expression const& else if (Identifier const* identifier = std::get_if(&_value)) return explore(identifier->name); else if (FunctionCall const* f = get_if(&_value)) - if (f->functionName.name == "add"_yulstring || f->functionName.name == "sub"_yulstring) + { + if (f->functionName.name == "add"_yulstring) + { if (optional a = explore(f->arguments[0])) if (optional b = explore(f->arguments[1])) { - u256 offset = - f->functionName.name == "add"_yulstring ? - a->offset + b->offset : - a->offset - b->offset; - if (a->reference == b->reference) - // Offsets relative to the same reference variable - return VariableOffset{a->reference, offset}; - else if (a->reference == YulString{}) + u256 offset = a->offset + b->offset; + if (a->reference.empty()) // a is constant return VariableOffset{b->reference, offset}; - else if (b->reference == YulString{}) + else if (b->reference.empty()) // b is constant return VariableOffset{a->reference, offset}; } + } + else if (f->functionName.name == "sub"_yulstring) + if (optional a = explore(f->arguments[0])) + if (optional b = explore(f->arguments[1])) + { + u256 offset = a->offset - b->offset; + if (a->reference == b->reference) + return VariableOffset{YulString{}, offset}; + else if (b->reference.empty()) + // b is constant + return VariableOffset{a->reference, offset}; + } + } return {}; } From 695b250557e1588147d7658455617327a900b95b Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 10 Nov 2022 17:14:01 +0100 Subject: [PATCH 033/228] Update gas costs. --- .../array/copying/function_type_array_to_storage.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/libsolidity/semanticTests/array/copying/function_type_array_to_storage.sol b/test/libsolidity/semanticTests/array/copying/function_type_array_to_storage.sol index 1e8760250a37..409bb2983cc8 100644 --- a/test/libsolidity/semanticTests/array/copying/function_type_array_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/function_type_array_to_storage.sol @@ -46,7 +46,7 @@ contract C { } // ---- // test() -> 0x20, 0x14, "[a called][b called]" -// gas irOptimized: 116673 +// gas irOptimized: 116660 // gas legacy: 119030 // gas legacyOptimized: 117021 // test2() -> 0x20, 0x14, "[b called][a called]" From 3ac6edec5b88fc5f11f56f141242b1703e0aa935 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 10 Nov 2022 18:02:56 +0100 Subject: [PATCH 034/228] Apply suggestions from code review --- libyul/optimiser/KnowledgeBase.cpp | 33 ++++++++++++++++++------------ 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/libyul/optimiser/KnowledgeBase.cpp b/libyul/optimiser/KnowledgeBase.cpp index 558dc8988800..96b352e402d9 100644 --- a/libyul/optimiser/KnowledgeBase.cpp +++ b/libyul/optimiser/KnowledgeBase.cpp @@ -52,7 +52,7 @@ optional KnowledgeBase::differenceIfKnownConstant(YulString _a, YulString if (offA.reference == offB.reference) return offA.offset - offB.offset; else - return {}; + return nullopt; } @@ -66,13 +66,13 @@ bool KnowledgeBase::knownToBeDifferentByAtLeast32(YulString _a, YulString _b) bool KnowledgeBase::knownToBeZero(YulString _a) { - return valueIfKnownConstant(_a) == u256{}; + return valueIfKnownConstant(_a) == 0; } optional KnowledgeBase::valueIfKnownConstant(YulString _a) { VariableOffset offset = explore(_a); - if (offset.reference == YulString{}) + if (offset.reference.empty()) return offset.offset; else return nullopt; @@ -85,7 +85,7 @@ optional KnowledgeBase::valueIfKnownConstant(Expression const& _expression else if (Literal const* lit = get_if(&_expression)) return valueOfLiteral(*lit); else - return {}; + return nullopt; } KnowledgeBase::VariableOffset KnowledgeBase::explore(YulString _var) @@ -117,9 +117,9 @@ KnowledgeBase::VariableOffset KnowledgeBase::explore(YulString _var) optional KnowledgeBase::explore(Expression const& _value) { - if (Literal const* literal = std::get_if(&_value)) + if (Literal const* literal = get_if(&_value)) return VariableOffset{YulString{}, valueOfLiteral(*literal)}; - else if (Identifier const* identifier = std::get_if(&_value)) + else if (Identifier const* identifier = get_if(&_value)) return explore(identifier->name); else if (FunctionCall const* f = get_if(&_value)) { @@ -150,7 +150,7 @@ optional KnowledgeBase::explore(Expression const& } } - return {}; + return nullopt; } Expression const* KnowledgeBase::valueOf(YulString _var) @@ -175,26 +175,33 @@ void KnowledgeBase::reset(YulString _var) if (VariableOffset const* offset = util::valueOrNullptr(m_offsets, _var)) { // Remove var from its group - if (offset->reference != YulString{}) + if (!offset->reference.empty()) m_groupMembers[offset->reference].erase(_var); m_offsets.erase(_var); } if (set* group = util::valueOrNullptr(m_groupMembers, _var)) { // _var was a representative, we might have to find a new one. - if (group->empty()) - m_groupMembers.erase(_var); - else + if (!group->empty()) { YulString newRepresentative = *group->begin(); + yulAssert(newRepresentative != _var); u256 newOffset = m_offsets[newRepresentative].offset; + // newOffset = newRepresentative - _var for (YulString groupMember: *group) { yulAssert(m_offsets[groupMember].reference == _var); m_offsets[groupMember].reference = newRepresentative; - m_offsets[newRepresentative].offset -= newOffset; + // groupMember = _var + m_offsets[groupMember].offset (old) + // = newRepresentative - newOffset + m_offsets[groupMember].offset (old) + // so subtracting newOffset from .offset yields the original relation again, + // just with _var replaced by newRepresentative + m_offsets[groupMember].offset -= newOffset; } + m_groupMembers[newRepresentative] = std::move(*group); + } + m_groupMembers.erase(_var); } } @@ -203,7 +210,7 @@ KnowledgeBase::VariableOffset KnowledgeBase::setOffset(YulString _variable, Vari m_offsets[_variable] = _value; // Constants are not tracked in m_groupMembers because // the "representative" can never be reset. - if (_value.reference != YulString{}) + if (!_value.reference.empty()) m_groupMembers[_value.reference].insert(_variable); return _value; } From 29e4becd73c47d16724263e63b27fc7ab139a278 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 9 Feb 2023 11:21:37 +0100 Subject: [PATCH 035/228] Introduce helpers for VariableOffset. --- libyul/optimiser/KnowledgeBase.cpp | 14 +++++--------- libyul/optimiser/KnowledgeBase.h | 13 +++++++++++++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/libyul/optimiser/KnowledgeBase.cpp b/libyul/optimiser/KnowledgeBase.cpp index 96b352e402d9..e01090146d82 100644 --- a/libyul/optimiser/KnowledgeBase.cpp +++ b/libyul/optimiser/KnowledgeBase.cpp @@ -71,11 +71,7 @@ bool KnowledgeBase::knownToBeZero(YulString _a) optional KnowledgeBase::valueIfKnownConstant(YulString _a) { - VariableOffset offset = explore(_a); - if (offset.reference.empty()) - return offset.offset; - else - return nullopt; + return explore(_a).absoluteValue(); } optional KnowledgeBase::valueIfKnownConstant(Expression const& _expression) @@ -129,10 +125,10 @@ optional KnowledgeBase::explore(Expression const& if (optional b = explore(f->arguments[1])) { u256 offset = a->offset + b->offset; - if (a->reference.empty()) + if (a->isAbsolute()) // a is constant return VariableOffset{b->reference, offset}; - else if (b->reference.empty()) + else if (b->isAbsolute()) // b is constant return VariableOffset{a->reference, offset}; } @@ -144,7 +140,7 @@ optional KnowledgeBase::explore(Expression const& u256 offset = a->offset - b->offset; if (a->reference == b->reference) return VariableOffset{YulString{}, offset}; - else if (b->reference.empty()) + else if (b->isAbsolute()) // b is constant return VariableOffset{a->reference, offset}; } @@ -175,7 +171,7 @@ void KnowledgeBase::reset(YulString _var) if (VariableOffset const* offset = util::valueOrNullptr(m_offsets, _var)) { // Remove var from its group - if (!offset->reference.empty()) + if (!offset->isAbsolute()) m_groupMembers[offset->reference].erase(_var); m_offsets.erase(_var); } diff --git a/libyul/optimiser/KnowledgeBase.h b/libyul/optimiser/KnowledgeBase.h index 934b2e21ad94..258f85f5cddf 100644 --- a/libyul/optimiser/KnowledgeBase.h +++ b/libyul/optimiser/KnowledgeBase.h @@ -84,6 +84,19 @@ class KnowledgeBase { YulString reference; u256 offset; + + bool isAbsolute() const + { + return reference.empty(); + } + + std::optional absoluteValue() const + { + if (isAbsolute()) + return offset; + else + return std::nullopt; + } }; VariableOffset explore(YulString _var); From 627bbe2ea3eed9374975b34d4f4cb438a8fd4a46 Mon Sep 17 00:00:00 2001 From: "Rodrigo Q. Saramago" Date: Tue, 7 Feb 2023 23:53:38 +0100 Subject: [PATCH 036/228] Update stale issues policies --- .github/workflows/stale.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 18046d107cfc..4761c62ac22e 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -10,8 +10,8 @@ permissions: pull-requests: write env: - BEFORE_ISSUE_STALE: 334 - BEFORE_ISSUE_CLOSE: 0 #FIXME: change to 14 days + BEFORE_ISSUE_STALE: 90 + BEFORE_ISSUE_CLOSE: 7 BEFORE_PR_STALE: 14 BEFORE_PR_CLOSE: 7 @@ -28,14 +28,13 @@ jobs: This issue has been marked as stale due to inactivity for the last ${{ env.BEFORE_ISSUE_STALE }} days. It will be automatically closed in ${{ env.BEFORE_ISSUE_CLOSE }} days. close-issue-message: | - Hi everyone! This issue has been closed due to inactivity. + Hi everyone! This issue has been automatically closed due to inactivity. If you think this issue is still relevant in the latest Solidity version and you have something to [contribute](https://docs.soliditylang.org/en/latest/contributing.html), feel free to reopen. However, unless the issue is a concrete proposal that can be implemented, we recommend starting a language discussion on the [forum](https://forum.soliditylang.org) instead. - any-of-issue-labels: stale # TODO: remove this when we're done with closing ancient issues - ascending: true # TODO: remove this when we're done with closing ancient issues + ascending: true stale-issue-label: stale close-issue-label: closed-due-inactivity - exempt-issue-labels: 'bug :bug:,roadmap,selected-for-development,must have' + exempt-issue-labels: 'bug :bug:,epic,roadmap,selected-for-development,must have,must have eventually,SMT' stale-pr-message: | This pull request is stale because it has been open for ${{ env.BEFORE_PR_STALE }} days with no activity. It will be closed in ${{ env.BEFORE_PR_CLOSE }} days unless the `stale` label is removed. @@ -48,6 +47,5 @@ jobs: exempt-pr-labels: 'external contribution :star:,roadmap,epic' exempt-draft-pr: false exempt-all-milestones: true - # remove-stale-when-updated: true # TODO: uncomment and remove the line below when we're done with closing ancient issues - remove-issue-stale-when-updated: false + remove-stale-when-updated: true operations-per-run: 128 From a5166f0a19971fc778172ffb0fcd790afaf1592b Mon Sep 17 00:00:00 2001 From: "Rodrigo Q. Saramago" Date: Sat, 11 Feb 2023 17:16:03 +0100 Subject: [PATCH 037/228] gp2 external test workaround --- test/externalTests/common.sh | 3 +-- test/externalTests/gp2.sh | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/externalTests/common.sh b/test/externalTests/common.sh index b6a85c1e3b8c..97d0a337ee59 100644 --- a/test/externalTests/common.sh +++ b/test/externalTests/common.sh @@ -235,8 +235,7 @@ function force_truffle_compiler_settings function name_hardhat_default_export { local config_file="$1" - - local import="import {HardhatUserConfig} from 'hardhat/types';" + local import="import {HardhatUserConfig} from 'hardhat/types/config';" local config="const config: HardhatUserConfig = {" sed -i "s|^\s*export\s*default\s*{|${import}\n${config}|g" "$config_file" echo "export default config;" >> "$config_file" diff --git a/test/externalTests/gp2.sh b/test/externalTests/gp2.sh index 9e9ee72af24b..120379478ad4 100755 --- a/test/externalTests/gp2.sh +++ b/test/externalTests/gp2.sh @@ -76,6 +76,9 @@ function gp2_test # See https://github.com/cowprotocol/contracts/issues/32 yarn add @tenderly/hardhat-tenderly@1.1.6 + # Add missing sinon type definitions + yarn add @types/sinon + # Some dependencies come with pre-built artifacts. We want to build from scratch. rm -r node_modules/@gnosis.pm/safe-contracts/build/ From 7585cd95076b532e080a78f3242bb19853b7e909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 2 Feb 2023 14:47:03 +0100 Subject: [PATCH 038/228] ReleaseChecklist: List packages needed to update the PPA as prerequisites --- ReleaseChecklist.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 0e46522ba307..589f5ad4fa7f 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -7,6 +7,7 @@ - [ ] DockerHub account with push rights to the [``solc`` image](https://hub.docker.com/r/ethereum/solc). - [ ] Lauchpad (Ubuntu One) account with a membership in the ["Ethereum" team](https://launchpad.net/~ethereum) and a gnupg key for your email in the ``ethereum.org`` domain (has to be version 1, gpg2 won't work). + - [ ] Ubuntu/Debian dependencies of the PPA scripts: ``devscripts``, ``debhelper``, ``dput``, ``git``, ``wget``, ``ca-certificates``. - [ ] [npm Registry](https://www.npmjs.com) account added as a collaborator for the [``solc`` package](https://www.npmjs.com/package/solc). - [ ] Access to the [solidity_lang Twitter account](https://twitter.com/solidity_lang). - [ ] [Reddit](https://www.reddit.com) account that is at least 10 days old with a minimum of 20 comment karma (``/r/ethereum`` requirements). From 8b7879c7dad9d3850546ea2a8b34742033b04f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 2 Feb 2023 14:47:31 +0100 Subject: [PATCH 039/228] ReleaseChecklist: `static-z3.sh` is actually called `static_z3.sh` --- ReleaseChecklist.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 589f5ad4fa7f..8c38bd8e4741 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -58,9 +58,9 @@ ### PPA - [ ] Create ``.release_ppa_auth`` at the root of your local Solidity checkout and set ``LAUNCHPAD_EMAIL`` and ``LAUNCHPAD_KEYID`` to your key's email and key id. - - [ ] Double-check that the ``DISTRIBUTIONS`` list in ``scripts/release_ppa.sh`` and ``scripts/deps-ppa/static-z3.sh`` contains the most recent versions of Ubuntu. + - [ ] Double-check that the ``DISTRIBUTIONS`` list in ``scripts/release_ppa.sh`` and ``scripts/deps-ppa/static_z3.sh`` contains the most recent versions of Ubuntu. - [ ] Make sure the [``~ethereum/cpp-build-deps`` PPA repository](https://launchpad.net/~ethereum/+archive/ubuntu/cpp-build-deps) contains ``libz3-static-dev builds`` for all current versions of Ubuntu. - If not, run ``scripts/deps-ppa/static-z3.sh`` and wait for the builds to succeed before continuing. + If not, run ``scripts/deps-ppa/static_z3.sh`` and wait for the builds to succeed before continuing. - [ ] Run ``scripts/release_ppa.sh v$VERSION`` to create the PPA release. - [ ] Wait for the [``~ethereum/ethereum-static`` PPA](https://launchpad.net/~ethereum/+archive/ubuntu/ethereum-static) build to be finished and published for *all platforms*. **SERIOUSLY: DO NOT PROCEED EARLIER!!!** From 89c4ee69420612b18e6637e5b20213595d45c069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 3 Feb 2023 19:01:22 +0100 Subject: [PATCH 040/228] Add a script for generating a list of contributors to paste in release notes --- ReleaseChecklist.md | 4 ++- scripts/list_contributors.sh | 51 ++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100755 scripts/list_contributors.sh diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 8c38bd8e4741..f5b5e3ada064 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -33,7 +33,9 @@ Set the target to the ``develop`` branch and the tag to the new version, e.g. ``v0.8.5``. Include the following warning: ``**The release is still in progress and the binaries may not yet be available from all sources.**``. Don't publish it yet - click the ``Save draft`` button instead. - - [ ] Thank voluntary contributors in the GitHub release notes (use ``git shortlog --summary --email v0.5.3..origin/develop``). + - [ ] Thank voluntary contributors in the GitHub release notes. + Use ``scripts/list_contributors.sh v`` to get initial list of names. + Remove different variants of the same name manually before using the output. - [ ] Check that all tests on the latest commit in ``develop`` are green. - [ ] Click the ``Publish release`` button on the release page, creating the tag. - [ ] Wait for the CI runs on the tag itself. diff --git a/scripts/list_contributors.sh b/scripts/list_contributors.sh new file mode 100755 index 000000000000..4b8f06d7654b --- /dev/null +++ b/scripts/list_contributors.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +# ------------------------------------------------------------------------------ +# Creates a list containing names of people who contributed to the project, for use +# in release notes. The names come from the author field on the commits between +# the current revision and the one specified as argument. +# +# Note that the output often requires extra manual processing to remove entries +# that refer to the same person (diacritics vs no diacritics, name vs nickname, etc.). +# +# Usage: +#