/src/solidity/libevmasm/KnownState.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | This file is part of solidity. |
3 | | |
4 | | solidity is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation, either version 3 of the License, or |
7 | | (at your option) any later version. |
8 | | |
9 | | solidity is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with solidity. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | // SPDX-License-Identifier: GPL-3.0 |
18 | | /** |
19 | | * @file KnownState.h |
20 | | * @author Christian <c@ethdev.com> |
21 | | * @date 2015 |
22 | | * Contains knowledge about the state of the virtual machine at a specific instruction. |
23 | | */ |
24 | | |
25 | | #pragma once |
26 | | |
27 | | #if defined(__clang__) |
28 | | #pragma clang diagnostic push |
29 | | #pragma clang diagnostic ignored "-Wredeclared-class-member" |
30 | | #endif // defined(__clang__) |
31 | | |
32 | | // Disable warning about nodiscard. Could be a compiler bug, might be removed in the future. |
33 | | #if defined(_MSC_VER) |
34 | | #pragma warning(push) |
35 | | #pragma warning(disable: 4834) |
36 | | #endif |
37 | | |
38 | | #include <boost/bimap.hpp> |
39 | | |
40 | | #if defined(_MSC_VER) |
41 | | #pragma warning(pop) |
42 | | #endif |
43 | | |
44 | | #if defined(__clang__) |
45 | | #pragma clang diagnostic pop |
46 | | #endif // defined(__clang__) |
47 | | |
48 | | #include <libsolutil/CommonIO.h> |
49 | | #include <libsolutil/Exceptions.h> |
50 | | #include <libevmasm/ExpressionClasses.h> |
51 | | #include <libevmasm/SemanticInformation.h> |
52 | | |
53 | | #include <limits> |
54 | | #include <utility> |
55 | | #include <vector> |
56 | | #include <map> |
57 | | #include <set> |
58 | | #include <tuple> |
59 | | #include <memory> |
60 | | #include <ostream> |
61 | | |
62 | | namespace solidity::langutil |
63 | | { |
64 | | struct SourceLocation; |
65 | | } |
66 | | |
67 | | namespace solidity::evmasm |
68 | | { |
69 | | |
70 | | class AssemblyItem; |
71 | | using AssemblyItems = std::vector<AssemblyItem>; |
72 | | |
73 | | /** |
74 | | * Class to infer and store knowledge about the state of the virtual machine at a specific |
75 | | * instruction. |
76 | | * |
77 | | * The general workings are that for each assembly item that is fed, an equivalence class is |
78 | | * derived from the operation and the equivalence class of its arguments. DUPi, SWAPi and some |
79 | | * arithmetic instructions are used to infer equivalences while these classes are determined. |
80 | | */ |
81 | | class KnownState |
82 | | { |
83 | | public: |
84 | | using Id = ExpressionClasses::Id; |
85 | | struct StoreOperation |
86 | | { |
87 | | enum Target { Invalid, Memory, Storage }; |
88 | | |
89 | 0 | bool isValid() const { return target != Invalid; } |
90 | | |
91 | | Target target{Invalid}; |
92 | | Id slot{std::numeric_limits<Id>::max()}; |
93 | | unsigned sequenceNumber{std::numeric_limits<unsigned>::max()}; |
94 | | Id expression{std::numeric_limits<Id>::max()}; |
95 | | }; |
96 | | |
97 | | explicit KnownState( |
98 | | std::shared_ptr<ExpressionClasses> _expressionClasses = std::make_shared<ExpressionClasses>() |
99 | | ): m_expressionClasses(std::move(_expressionClasses)) |
100 | 0 | { |
101 | 0 | } |
102 | | |
103 | | /// Streams debugging information to @a _out. |
104 | | std::ostream& stream(std::ostream& _out) const; |
105 | | |
106 | | /// Feeds the item into the system for analysis. |
107 | | /// @returns a possible store operation |
108 | | StoreOperation feedItem(AssemblyItem const& _item, bool _copyItem = false); |
109 | | |
110 | | /// Resets any knowledge about storage. |
111 | 0 | void resetStorage() { m_storageContent.clear(); } |
112 | | /// Resets any knowledge about storage. |
113 | 0 | void resetMemory() { m_memoryContent.clear(); } |
114 | | /// Resets known Keccak-256 hashes |
115 | 0 | void resetKnownKeccak256Hashes() { m_knownKeccak256Hashes.clear(); } |
116 | | /// Resets any knowledge about the current stack. |
117 | 0 | void resetStack() { m_stackElements.clear(); m_stackHeight = 0; } |
118 | | /// Resets any knowledge. |
119 | 0 | void reset() { resetStorage(); resetMemory(); resetKnownKeccak256Hashes(); resetStack(); } |
120 | | |
121 | 0 | unsigned sequenceNumber() const { return m_sequenceNumber; } |
122 | | |
123 | | /// Replaces the state by the intersection with _other, i.e. only equal knowledge is retained. |
124 | | /// If the stack heighht is different, the smaller one is used and the stack is compared |
125 | | /// relatively. |
126 | | /// @param _combineSequenceNumbers if true, sets the sequence number to the maximum of both |
127 | | void reduceToCommonKnowledge(KnownState const& _other, bool _combineSequenceNumbers); |
128 | | |
129 | | /// @returns a shared pointer to a copy of this state. |
130 | 0 | std::shared_ptr<KnownState> copy() const { return std::make_shared<KnownState>(*this); } |
131 | | |
132 | | /// @returns true if the knowledge about the state of both objects is (known to be) equal. |
133 | | bool operator==(KnownState const& _other) const; |
134 | | |
135 | | /// Retrieves the current equivalence class for the given stack element (or generates a new |
136 | | /// one if it does not exist yet). |
137 | | Id stackElement(int _stackHeight, langutil::SourceLocation const& _location); |
138 | | /// @returns the stackElement relative to the current stack height. |
139 | | Id relativeStackElement(int _stackOffset, langutil::SourceLocation const& _location = {}); |
140 | | |
141 | | /// @returns its set of tags if the given expression class is a known tag union; returns a set |
142 | | /// containing the tag if it is a PushTag expression and the empty set otherwise. |
143 | | std::set<u256> tagsInExpression(Id _expressionId); |
144 | | /// During analysis, different tags on the stack are partially treated as the same class. |
145 | | /// This removes such classes not to confuse later analyzers. |
146 | | void clearTagUnions(); |
147 | | |
148 | 0 | int stackHeight() const { return m_stackHeight; } |
149 | 0 | std::map<int, Id> const& stackElements() const { return m_stackElements; } |
150 | 0 | ExpressionClasses& expressionClasses() const { return *m_expressionClasses; } |
151 | | |
152 | 0 | std::map<Id, Id> const& storageContent() const { return m_storageContent; } |
153 | | |
154 | | private: |
155 | | /// Assigns a new equivalence class to the next sequence number of the given stack element. |
156 | | void setStackElement(int _stackHeight, Id _class); |
157 | | /// Swaps the given stack elements in their next sequence number. |
158 | | void swapStackElements(int _stackHeightA, int _stackHeightB, langutil::SourceLocation const& _location); |
159 | | |
160 | | /// Increments the sequence number, deletes all storage information that might be overwritten |
161 | | /// and stores the new value at the given slot. |
162 | | /// @returns the store operation, which might be invalid if storage was not modified |
163 | | StoreOperation storeInStorage(Id _slot, Id _value, langutil::SourceLocation const& _location); |
164 | | /// Retrieves the current value at the given slot in storage or creates a new special sload class. |
165 | | Id loadFromStorage(Id _slot, langutil::SourceLocation const& _location); |
166 | | /// Increments the sequence number, deletes all memory information that might be overwritten |
167 | | /// and stores the new value at the given slot. |
168 | | /// @returns the store operation, which might be invalid if memory was not modified |
169 | | StoreOperation storeInMemory(Id _slot, Id _value, langutil::SourceLocation const& _location); |
170 | | /// Retrieves the current value at the given slot in memory or creates a new special mload class. |
171 | | Id loadFromMemory(Id _slot, langutil::SourceLocation const& _location); |
172 | | /// Finds or creates a new expression that applies the Keccak-256 hash function to the contents in memory. |
173 | | Id applyKeccak256(Id _start, Id _length, langutil::SourceLocation const& _location); |
174 | | |
175 | | /// @returns a new or already used Id representing the given set of tags. |
176 | | Id tagUnion(std::set<u256> _tags); |
177 | | |
178 | | /// Current stack height, can be negative. |
179 | | int m_stackHeight = 0; |
180 | | /// Current stack layout, mapping stack height -> equivalence class |
181 | | std::map<int, Id> m_stackElements; |
182 | | /// Current sequence number, this is incremented with each modification to storage or memory. |
183 | | unsigned m_sequenceNumber = 1; |
184 | | /// Knowledge about storage content. |
185 | | std::map<Id, Id> m_storageContent; |
186 | | /// Knowledge about memory content. Keys are memory addresses, note that the values overlap |
187 | | /// and are not contained here if they are not completely known. |
188 | | std::map<Id, Id> m_memoryContent; |
189 | | /// Keeps record of all Keccak-256 hashes that are computed. The first parameter in the |
190 | | /// std::pair corresponds to memory content and the second parameter corresponds to the length |
191 | | /// that is accessed. |
192 | | std::map<std::pair<std::vector<Id>, unsigned>, Id> m_knownKeccak256Hashes; |
193 | | /// Structure containing the classes of equivalent expressions. |
194 | | std::shared_ptr<ExpressionClasses> m_expressionClasses; |
195 | | /// Container for unions of tags stored on the stack. |
196 | | boost::bimap<Id, std::set<u256>> m_tagUnions; |
197 | | }; |
198 | | |
199 | | } |