/src/solidity/libsolidity/formal/SymbolicState.cpp
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 | | #include <libsolidity/formal/SymbolicState.h> |
20 | | |
21 | | #include <libsolidity/formal/SymbolicTypes.h> |
22 | | #include <libsolidity/formal/EncodingContext.h> |
23 | | #include <libsolidity/formal/SMTEncoder.h> |
24 | | |
25 | | using namespace std; |
26 | | using namespace solidity; |
27 | | using namespace solidity::smtutil; |
28 | | using namespace solidity::frontend::smt; |
29 | | |
30 | | BlockchainVariable::BlockchainVariable( |
31 | | string _name, |
32 | | map<string, smtutil::SortPointer> _members, |
33 | | EncodingContext& _context |
34 | | ): |
35 | | m_name(move(_name)), |
36 | | m_members(move(_members)), |
37 | | m_context(_context) |
38 | 46.9k | { |
39 | 46.9k | vector<string> members; |
40 | 46.9k | vector<SortPointer> sorts; |
41 | 46.9k | for (auto const& [component, sort]: m_members) |
42 | 158k | { |
43 | 158k | members.emplace_back(component); |
44 | 158k | sorts.emplace_back(sort); |
45 | 158k | m_componentIndices[component] = static_cast<unsigned>(members.size() - 1); |
46 | 158k | } |
47 | 46.9k | m_tuple = make_unique<SymbolicTupleVariable>( |
48 | 46.9k | make_shared<smtutil::TupleSort>(m_name + "_type", members, sorts), |
49 | 46.9k | m_name, |
50 | 46.9k | m_context |
51 | 46.9k | ); |
52 | 46.9k | } |
53 | | |
54 | | smtutil::Expression BlockchainVariable::member(string const& _member) const |
55 | 519k | { |
56 | 519k | return m_tuple->component(m_componentIndices.at(_member)); |
57 | 519k | } |
58 | | |
59 | | smtutil::Expression BlockchainVariable::assignMember(string const& _member, smtutil::Expression const& _value) |
60 | 11.4k | { |
61 | 11.4k | vector<smtutil::Expression> args; |
62 | 11.4k | for (auto const& m: m_members) |
63 | 11.4k | if (m.first == _member) |
64 | 11.4k | args.emplace_back(_value); |
65 | 0 | else |
66 | 0 | args.emplace_back(member(m.first)); |
67 | 11.4k | m_tuple->increaseIndex(); |
68 | 11.4k | auto tuple = m_tuple->currentValue(); |
69 | 11.4k | auto sortExpr = smtutil::Expression(make_shared<smtutil::SortSort>(tuple.sort), tuple.name); |
70 | 11.4k | m_context.addAssertion(tuple == smtutil::Expression::tuple_constructor(sortExpr, args)); |
71 | 11.4k | return m_tuple->currentValue(); |
72 | 11.4k | } |
73 | | |
74 | | void SymbolicState::reset() |
75 | 208k | { |
76 | 208k | m_error.resetIndex(); |
77 | 208k | m_thisAddress.resetIndex(); |
78 | 208k | m_state.reset(); |
79 | 208k | m_tx.reset(); |
80 | 208k | m_crypto.reset(); |
81 | 208k | if (m_abi) |
82 | 200k | m_abi->reset(); |
83 | 208k | } |
84 | | |
85 | | smtutil::Expression SymbolicState::balances() const |
86 | 42.3k | { |
87 | 42.3k | return m_state.member("balances"); |
88 | 42.3k | } |
89 | | |
90 | | smtutil::Expression SymbolicState::balance() const |
91 | 169 | { |
92 | 169 | return balance(thisAddress()); |
93 | 169 | } |
94 | | |
95 | | smtutil::Expression SymbolicState::balance(smtutil::Expression _address) const |
96 | 31.8k | { |
97 | 31.8k | return smtutil::Expression::select(balances(), move(_address)); |
98 | 31.8k | } |
99 | | |
100 | | smtutil::Expression SymbolicState::blockhash(smtutil::Expression _blockNumber) const |
101 | 26 | { |
102 | 26 | return smtutil::Expression::select(m_tx.member("blockhash"), move(_blockNumber)); |
103 | 26 | } |
104 | | |
105 | | void SymbolicState::newBalances() |
106 | 842 | { |
107 | 842 | auto tupleSort = dynamic_pointer_cast<TupleSort>(stateSort()); |
108 | 842 | auto balanceSort = tupleSort->components.at(tupleSort->memberToIndex.at("balances")); |
109 | 842 | SymbolicVariable newBalances(balanceSort, "fresh_balances_" + to_string(m_context.newUniqueId()), m_context); |
110 | 842 | m_state.assignMember("balances", newBalances.currentValue()); |
111 | 842 | } |
112 | | |
113 | | void SymbolicState::transfer(smtutil::Expression _from, smtutil::Expression _to, smtutil::Expression _value) |
114 | 112 | { |
115 | 112 | unsigned indexBefore = m_state.index(); |
116 | 112 | addBalance(_from, 0 - _value); |
117 | 112 | addBalance(_to, move(_value)); |
118 | 112 | unsigned indexAfter = m_state.index(); |
119 | 112 | solAssert(indexAfter > indexBefore, ""); |
120 | 112 | m_state.newVar(); |
121 | | /// Do not apply the transfer operation if _from == _to. |
122 | 112 | auto newState = smtutil::Expression::ite( |
123 | 112 | move(_from) == move(_to), |
124 | 112 | m_state.value(indexBefore), |
125 | 112 | m_state.value(indexAfter) |
126 | 112 | ); |
127 | 112 | m_context.addAssertion(m_state.value() == newState); |
128 | 112 | } |
129 | | |
130 | | void SymbolicState::addBalance(smtutil::Expression _address, smtutil::Expression _value) |
131 | 10.5k | { |
132 | 10.5k | auto newBalances = smtutil::Expression::store( |
133 | 10.5k | balances(), |
134 | 10.5k | _address, |
135 | 10.5k | balance(_address) + move(_value) |
136 | 10.5k | ); |
137 | 10.5k | m_state.assignMember("balances", newBalances); |
138 | 10.5k | } |
139 | | |
140 | | smtutil::Expression SymbolicState::txMember(string const& _member) const |
141 | 25.0k | { |
142 | 25.0k | return m_tx.member(_member); |
143 | 25.0k | } |
144 | | |
145 | | smtutil::Expression SymbolicState::txTypeConstraints() const |
146 | 32.7k | { |
147 | 32.7k | return |
148 | 32.7k | smt::symbolicUnknownConstraints(m_tx.member("block.basefee"), TypeProvider::uint256()) && |
149 | 32.7k | smt::symbolicUnknownConstraints(m_tx.member("block.chainid"), TypeProvider::uint256()) && |
150 | 32.7k | smt::symbolicUnknownConstraints(m_tx.member("block.coinbase"), TypeProvider::address()) && |
151 | 32.7k | smt::symbolicUnknownConstraints(m_tx.member("block.difficulty"), TypeProvider::uint256()) && |
152 | 32.7k | smt::symbolicUnknownConstraints(m_tx.member("block.gaslimit"), TypeProvider::uint256()) && |
153 | 32.7k | smt::symbolicUnknownConstraints(m_tx.member("block.number"), TypeProvider::uint256()) && |
154 | 32.7k | smt::symbolicUnknownConstraints(m_tx.member("block.timestamp"), TypeProvider::uint256()) && |
155 | 32.7k | smt::symbolicUnknownConstraints(m_tx.member("msg.sender"), TypeProvider::address()) && |
156 | 32.7k | smt::symbolicUnknownConstraints(m_tx.member("msg.value"), TypeProvider::uint256()) && |
157 | 32.7k | smt::symbolicUnknownConstraints(m_tx.member("tx.origin"), TypeProvider::address()) && |
158 | 32.7k | smt::symbolicUnknownConstraints(m_tx.member("tx.gasprice"), TypeProvider::uint256()); |
159 | 32.7k | } |
160 | | |
161 | | smtutil::Expression SymbolicState::txNonPayableConstraint() const |
162 | 31.2k | { |
163 | 31.2k | return m_tx.member("msg.value") == 0; |
164 | 31.2k | } |
165 | | |
166 | | smtutil::Expression SymbolicState::txFunctionConstraints(FunctionDefinition const& _function) const |
167 | 22.1k | { |
168 | 22.1k | smtutil::Expression conj = _function.isPayable() ? smtutil::Expression(true) : txNonPayableConstraint(); |
169 | 22.1k | if (_function.isPartOfExternalInterface()) |
170 | 20.2k | { |
171 | 20.2k | auto sig = TypeProvider::function(_function)->externalIdentifier(); |
172 | 20.2k | conj = conj && m_tx.member("msg.sig") == sig; |
173 | 20.2k | auto b0 = sig >> (3 * 8); |
174 | 20.2k | auto b1 = (sig & 0x00ff0000) >> (2 * 8); |
175 | 20.2k | auto b2 = (sig & 0x0000ff00) >> (1 * 8); |
176 | 20.2k | auto b3 = (sig & 0x000000ff); |
177 | 20.2k | auto data = smtutil::Expression::tuple_get(m_tx.member("msg.data"), 0); |
178 | 20.2k | conj = conj && smtutil::Expression::select(data, 0) == b0; |
179 | 20.2k | conj = conj && smtutil::Expression::select(data, 1) == b1; |
180 | 20.2k | conj = conj && smtutil::Expression::select(data, 2) == b2; |
181 | 20.2k | conj = conj && smtutil::Expression::select(data, 3) == b3; |
182 | 20.2k | auto length = smtutil::Expression::tuple_get(m_tx.member("msg.data"), 1); |
183 | | // TODO add ABI size of function input parameters here \/ |
184 | 20.2k | conj = conj && length >= 4; |
185 | 20.2k | } |
186 | | |
187 | 22.1k | return conj; |
188 | 22.1k | } |
189 | | |
190 | | void SymbolicState::prepareForSourceUnit(SourceUnit const& _source) |
191 | 21.9k | { |
192 | 21.9k | set<FunctionCall const*> abiCalls = SMTEncoder::collectABICalls(&_source); |
193 | 21.9k | for (auto const& source: _source.referencedSourceUnits(true)) |
194 | 1.64k | abiCalls += SMTEncoder::collectABICalls(source); |
195 | 21.9k | buildABIFunctions(abiCalls); |
196 | 21.9k | } |
197 | | |
198 | | /// Private helpers. |
199 | | |
200 | | void SymbolicState::buildABIFunctions(set<FunctionCall const*> const& _abiFunctions) |
201 | 21.9k | { |
202 | 21.9k | map<string, SortPointer> functions; |
203 | | |
204 | 21.9k | for (auto const* funCall: _abiFunctions) |
205 | 308 | { |
206 | 308 | auto t = dynamic_cast<FunctionType const*>(funCall->expression().annotation().type); |
207 | | |
208 | 308 | auto const& args = funCall->sortedArguments(); |
209 | 308 | auto const& paramTypes = t->parameterTypes(); |
210 | 308 | auto const& returnTypes = t->returnParameterTypes(); |
211 | | |
212 | | |
213 | 308 | auto argTypes = [](auto const& _args) { |
214 | 280 | return util::applyMap(_args, [](auto arg) { return arg->annotation().type; }); |
215 | 208 | }; |
216 | | |
217 | | /// Since each abi.* function may have a different number of input/output parameters, |
218 | | /// we generically compute those types. |
219 | 308 | vector<frontend::Type const*> inTypes; |
220 | 308 | vector<frontend::Type const*> outTypes; |
221 | 308 | if (t->kind() == FunctionType::Kind::ABIDecode) |
222 | 96 | { |
223 | | /// abi.decode : (bytes, tuple_of_types(return_types)) -> (return_types) |
224 | 96 | solAssert(args.size() == 2, "Unexpected number of arguments for abi.decode"); |
225 | 96 | inTypes.emplace_back(TypeProvider::bytesMemory()); |
226 | 96 | auto argType = args.at(1)->annotation().type; |
227 | 96 | if (auto const* tupleType = dynamic_cast<TupleType const*>(argType)) |
228 | 22 | for (auto componentType: tupleType->components()) |
229 | 50 | { |
230 | 50 | auto typeType = dynamic_cast<TypeType const*>(componentType); |
231 | 50 | solAssert(typeType, ""); |
232 | 50 | outTypes.emplace_back(typeType->actualType()); |
233 | 50 | } |
234 | 74 | else if (auto const* typeType = dynamic_cast<TypeType const*>(argType)) |
235 | 74 | outTypes.emplace_back(typeType->actualType()); |
236 | 0 | else |
237 | 74 | solAssert(false, "Unexpected argument of abi.decode"); |
238 | 96 | } |
239 | 212 | else if (t->kind() == FunctionType::Kind::ABIEncodeCall) |
240 | 4 | { |
241 | | // abi.encodeCall : (functionPointer, tuple_of_args_or_one_non_tuple_arg(arguments)) -> bytes |
242 | 4 | solAssert(args.size() == 2, "Unexpected number of arguments for abi.encodeCall"); |
243 | | |
244 | 4 | outTypes.emplace_back(TypeProvider::bytesMemory()); |
245 | 4 | inTypes.emplace_back(args.at(0)->annotation().type); |
246 | 4 | inTypes.emplace_back(args.at(1)->annotation().type); |
247 | 4 | } |
248 | 208 | else |
249 | 208 | { |
250 | 208 | outTypes = returnTypes; |
251 | 208 | if ( |
252 | 208 | t->kind() == FunctionType::Kind::ABIEncodeWithSelector || |
253 | 208 | t->kind() == FunctionType::Kind::ABIEncodeWithSignature |
254 | 208 | ) |
255 | 42 | { |
256 | | /// abi.encodeWithSelector : (bytes4, one_or_more_types) -> bytes |
257 | | /// abi.encodeWithSignature : (string, one_or_more_types) -> bytes |
258 | 42 | inTypes.emplace_back(paramTypes.front()); |
259 | 42 | inTypes += argTypes(vector<ASTPointer<Expression const>>(args.begin() + 1, args.end())); |
260 | 42 | } |
261 | 166 | else |
262 | 166 | { |
263 | | /// abi.encode/abi.encodePacked : one_or_more_types -> bytes |
264 | 166 | solAssert( |
265 | 166 | t->kind() == FunctionType::Kind::ABIEncode || |
266 | 166 | t->kind() == FunctionType::Kind::ABIEncodePacked, |
267 | 166 | "" |
268 | 166 | ); |
269 | 166 | inTypes = argTypes(args); |
270 | 166 | } |
271 | 208 | } |
272 | | |
273 | | /// Rational numbers and string literals add the concrete values to the type name, |
274 | | /// so we replace them by uint256 and bytes since those are the same as their SMT types. |
275 | | /// TODO we could also replace all types by their ABI type. |
276 | 616 | auto replaceTypes = [](auto& _types) { |
277 | 616 | for (auto& t: _types) |
278 | 762 | if (t->category() == frontend::Type::Category::RationalNumber) |
279 | 46 | t = TypeProvider::uint256(); |
280 | 716 | else if (t->category() == frontend::Type::Category::StringLiteral) |
281 | 26 | t = TypeProvider::bytesMemory(); |
282 | 690 | else if (auto userType = dynamic_cast<UserDefinedValueType const*>(t)) |
283 | 6 | t = &userType->underlyingType(); |
284 | 616 | }; |
285 | 308 | replaceTypes(inTypes); |
286 | 308 | replaceTypes(outTypes); |
287 | | |
288 | 308 | auto name = t->richIdentifier(); |
289 | 308 | for (auto paramType: inTypes + outTypes) |
290 | 762 | name += "_" + paramType->richIdentifier(); |
291 | | |
292 | 308 | m_abiMembers[funCall] = {name, inTypes, outTypes}; |
293 | | |
294 | 308 | if (functions.count(name)) |
295 | 52 | continue; |
296 | | |
297 | | /// If there is only one input or output parameter, we use that type directly. |
298 | | /// Otherwise we create a tuple wrapping the necessary input or output types. |
299 | 512 | auto typesToSort = [](auto const& _types, string const& _name) -> shared_ptr<Sort> { |
300 | 512 | if (_types.size() == 1) |
301 | 414 | return smtSortAbstractFunction(*_types.front()); |
302 | | |
303 | 98 | vector<string> inNames; |
304 | 98 | vector<SortPointer> sorts; |
305 | 324 | for (unsigned i = 0; i < _types.size(); ++i) |
306 | 226 | { |
307 | 226 | inNames.emplace_back(_name + "_input_" + to_string(i)); |
308 | 226 | sorts.emplace_back(smtSortAbstractFunction(*_types.at(i))); |
309 | 226 | } |
310 | 98 | return make_shared<smtutil::TupleSort>( |
311 | 98 | _name + "_input", |
312 | 98 | inNames, |
313 | 98 | sorts |
314 | 98 | ); |
315 | 512 | }; |
316 | | |
317 | 256 | auto functionSort = make_shared<smtutil::ArraySort>( |
318 | 256 | typesToSort(inTypes, name), |
319 | 256 | typesToSort(outTypes, name) |
320 | 256 | ); |
321 | | |
322 | 256 | functions[name] = functionSort; |
323 | 256 | } |
324 | | |
325 | 21.9k | m_abi = make_unique<BlockchainVariable>("abi", move(functions), m_context); |
326 | 21.9k | } |
327 | | |
328 | | smtutil::Expression SymbolicState::abiFunction(frontend::FunctionCall const* _funCall) |
329 | 323 | { |
330 | 323 | solAssert(m_abi, ""); |
331 | 323 | return m_abi->member(get<0>(m_abiMembers.at(_funCall))); |
332 | 323 | } |
333 | | |
334 | | SymbolicState::SymbolicABIFunction const& SymbolicState::abiFunctionTypes(FunctionCall const* _funCall) const |
335 | 323 | { |
336 | 323 | return m_abiMembers.at(_funCall); |
337 | 323 | } |