/src/solidity/libsolidity/formal/SMTEncoder.cpp
Line | Count | Source |
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/SMTEncoder.h> |
20 | | |
21 | | #include <libsolidity/ast/TypeProvider.h> |
22 | | #include <libsolidity/formal/SymbolicState.h> |
23 | | #include <libsolidity/formal/SymbolicTypes.h> |
24 | | |
25 | | #include <libsolidity/analysis/ConstantEvaluator.h> |
26 | | |
27 | | #include <libyul/AST.h> |
28 | | #include <libyul/optimiser/Semantics.h> |
29 | | |
30 | | #include <libsmtutil/SMTPortfolio.h> |
31 | | #include <libsmtutil/Helpers.h> |
32 | | |
33 | | #include <liblangutil/CharStreamProvider.h> |
34 | | |
35 | | #include <libsolutil/Algorithms.h> |
36 | | #include <libsolutil/FunctionSelector.h> |
37 | | |
38 | | #include <range/v3/view.hpp> |
39 | | |
40 | | #include <limits> |
41 | | #include <deque> |
42 | | |
43 | | using namespace solidity; |
44 | | using namespace solidity::util; |
45 | | using namespace solidity::langutil; |
46 | | using namespace solidity::frontend; |
47 | | using namespace std::string_literals; |
48 | | |
49 | | SMTEncoder::SMTEncoder( |
50 | | smt::EncodingContext& _context, |
51 | | ModelCheckerSettings _settings, |
52 | | UniqueErrorReporter& _errorReporter, |
53 | | UniqueErrorReporter& _unsupportedErrorReporter, |
54 | | ErrorReporter& _provedSafeReporter, |
55 | | langutil::CharStreamProvider const& _charStreamProvider |
56 | | ): |
57 | 25.1k | m_errorReporter(_errorReporter), |
58 | 25.1k | m_unsupportedErrors(_unsupportedErrorReporter), |
59 | 25.1k | m_provedSafeReporter(_provedSafeReporter), |
60 | 25.1k | m_context(_context), |
61 | 25.1k | m_settings(std::move(_settings)), |
62 | 25.1k | m_charStreamProvider(_charStreamProvider) |
63 | 25.1k | { |
64 | 25.1k | } |
65 | | |
66 | | void SMTEncoder::resetSourceAnalysis() |
67 | 26.8k | { |
68 | 26.8k | m_freeFunctions.clear(); |
69 | 26.8k | } |
70 | | |
71 | | bool SMTEncoder::visit(ContractDefinition const& _contract) |
72 | 27.8k | { |
73 | 27.8k | solAssert(m_currentContract, ""); |
74 | | |
75 | 27.8k | for (auto const& node: _contract.subNodes()) |
76 | 47.2k | if ( |
77 | 47.2k | !std::dynamic_pointer_cast<FunctionDefinition>(node) && |
78 | 20.5k | !std::dynamic_pointer_cast<VariableDeclaration>(node) |
79 | 47.2k | ) |
80 | 6.19k | node->accept(*this); |
81 | | |
82 | 27.8k | for (auto const& base: _contract.annotation().linearizedBaseContracts) |
83 | 31.3k | { |
84 | | // Look for all the constructor invocations bottom up. |
85 | 31.3k | if (auto const& constructor = base->constructor()) |
86 | 4.10k | for (auto const& invocation: constructor->modifiers()) |
87 | 712 | { |
88 | 712 | auto refDecl = invocation->name().annotation().referencedDeclaration; |
89 | 712 | if (auto const& baseContract = dynamic_cast<ContractDefinition const*>(refDecl)) |
90 | 522 | { |
91 | 522 | solAssert(!m_baseConstructorCalls.count(baseContract), ""); |
92 | 522 | m_baseConstructorCalls[baseContract] = invocation.get(); |
93 | 522 | } |
94 | 712 | } |
95 | 31.3k | } |
96 | | // Functions are visited first since they might be used |
97 | | // for state variable initialization which is part of |
98 | | // the constructor. |
99 | | // Constructors are visited as part of the constructor |
100 | | // hierarchy inlining. |
101 | 27.8k | for (auto const* function: contractFunctionsWithoutVirtual(_contract) + allFreeFunctions()) |
102 | 30.7k | if (!function->isConstructor()) |
103 | 26.6k | function->accept(*this); |
104 | | |
105 | | // Constructors need to be handled by the engines separately. |
106 | | |
107 | 27.8k | return false; |
108 | 27.8k | } |
109 | | |
110 | | void SMTEncoder::endVisit(ContractDefinition const& _contract) |
111 | 27.8k | { |
112 | 27.8k | m_context.resetAllVariables(); |
113 | | |
114 | 27.8k | m_baseConstructorCalls.clear(); |
115 | | |
116 | 27.8k | solAssert(m_currentContract == &_contract, ""); |
117 | 27.8k | m_currentContract = nullptr; |
118 | | |
119 | 27.8k | if (m_callStack.empty()) |
120 | 27.8k | m_context.popSolver(); |
121 | 27.8k | } |
122 | | |
123 | | bool SMTEncoder::visit(ImportDirective const&) |
124 | 1.67k | { |
125 | | // do not visit because the identifier therein will confuse us. |
126 | 1.67k | return false; |
127 | 1.67k | } |
128 | | |
129 | | void SMTEncoder::endVisit(VariableDeclaration const& _varDecl) |
130 | 50.8k | { |
131 | | // State variables are handled by the constructor. |
132 | 50.8k | if (_varDecl.isLocalVariable() &&_varDecl.value()) |
133 | 0 | assignment(_varDecl, *_varDecl.value()); |
134 | 50.8k | } |
135 | | |
136 | | bool SMTEncoder::visit(ModifierDefinition const&) |
137 | 742 | { |
138 | 742 | return false; |
139 | 742 | } |
140 | | |
141 | | bool SMTEncoder::visit(FunctionDefinition const& _function) |
142 | 32.3k | { |
143 | 32.3k | solAssert(m_currentContract, ""); |
144 | | |
145 | 32.3k | m_modifierDepthStack.push_back(-1); |
146 | | |
147 | 32.3k | initializeLocalVariables(_function); |
148 | | |
149 | 32.3k | _function.parameterList().accept(*this); |
150 | 32.3k | if (_function.returnParameterList()) |
151 | 32.3k | _function.returnParameterList()->accept(*this); |
152 | | |
153 | 32.3k | visitFunctionOrModifier(); |
154 | | |
155 | 32.3k | return false; |
156 | 32.3k | } |
157 | | |
158 | | void SMTEncoder::visitFunctionOrModifier() |
159 | 33.9k | { |
160 | 33.9k | solAssert(!m_callStack.empty(), ""); |
161 | 33.9k | solAssert(!m_modifierDepthStack.empty(), ""); |
162 | | |
163 | 33.9k | ++m_modifierDepthStack.back(); |
164 | 33.9k | FunctionDefinition const& function = dynamic_cast<FunctionDefinition const&>(*m_callStack.back().first); |
165 | | |
166 | 33.9k | if (m_modifierDepthStack.back() == static_cast<int>(function.modifiers().size())) |
167 | 32.4k | { |
168 | 32.4k | if (function.isImplemented()) |
169 | 31.7k | { |
170 | 31.7k | pushInlineFrame(function); |
171 | 31.7k | function.body().accept(*this); |
172 | 31.7k | popInlineFrame(function); |
173 | 31.7k | } |
174 | 32.4k | } |
175 | 1.50k | else |
176 | 1.50k | { |
177 | 1.50k | solAssert(m_modifierDepthStack.back() < static_cast<int>(function.modifiers().size()), ""); |
178 | 1.50k | ASTPointer<ModifierInvocation> const& modifierInvocation = |
179 | 1.50k | function.modifiers()[static_cast<size_t>(m_modifierDepthStack.back())]; |
180 | 1.50k | solAssert(modifierInvocation, ""); |
181 | 1.50k | auto refDecl = modifierInvocation->name().annotation().referencedDeclaration; |
182 | 1.50k | if (dynamic_cast<ContractDefinition const*>(refDecl)) |
183 | 522 | visitFunctionOrModifier(); |
184 | 979 | else if (auto modifier = resolveModifierInvocation(*modifierInvocation, m_currentContract)) |
185 | 979 | { |
186 | 979 | m_scopes.push_back(modifier); |
187 | 979 | inlineModifierInvocation(modifierInvocation.get(), modifier); |
188 | 979 | solAssert(m_scopes.back() == modifier, ""); |
189 | 979 | m_scopes.pop_back(); |
190 | 979 | } |
191 | 0 | else |
192 | 979 | solAssert(false, ""); |
193 | 1.50k | } |
194 | | |
195 | 33.9k | --m_modifierDepthStack.back(); |
196 | 33.9k | } |
197 | | |
198 | | void SMTEncoder::inlineModifierInvocation(ModifierInvocation const* _invocation, CallableDeclaration const* _definition) |
199 | 1.23k | { |
200 | 1.23k | solAssert(_invocation, ""); |
201 | 1.23k | _invocation->accept(*this); |
202 | | |
203 | 1.23k | std::vector<smtutil::Expression> args; |
204 | 1.23k | if (auto const* arguments = _invocation->arguments()) |
205 | 691 | { |
206 | 691 | auto const& modifierParams = _definition->parameters(); |
207 | 691 | solAssert(modifierParams.size() == arguments->size(), ""); |
208 | 1.36k | for (unsigned i = 0; i < arguments->size(); ++i) |
209 | 678 | args.push_back(expr(*arguments->at(i), modifierParams.at(i)->type())); |
210 | 691 | } |
211 | | |
212 | 1.23k | initializeFunctionCallParameters(*_definition, args); |
213 | | |
214 | 1.23k | pushCallStack({_definition, _invocation}); |
215 | 1.23k | pushInlineFrame(*_definition); |
216 | 1.23k | if (auto modifier = dynamic_cast<ModifierDefinition const*>(_definition)) |
217 | 979 | { |
218 | 979 | if (modifier->isImplemented()) |
219 | 957 | modifier->body().accept(*this); |
220 | 979 | popCallStack(); |
221 | 979 | } |
222 | 253 | else if (auto function = dynamic_cast<FunctionDefinition const*>(_definition)) |
223 | 253 | { |
224 | 253 | if (function->isImplemented()) |
225 | 253 | function->accept(*this); |
226 | | // Functions are popped from the callstack in endVisit(FunctionDefinition) |
227 | 253 | } |
228 | 1.23k | popInlineFrame(*_definition); |
229 | 1.23k | } |
230 | | |
231 | | void SMTEncoder::inlineConstructorHierarchy(ContractDefinition const& _contract) |
232 | 14.5k | { |
233 | 14.5k | auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts; |
234 | 14.5k | auto it = find(begin(hierarchy), end(hierarchy), &_contract); |
235 | 14.5k | solAssert(it != end(hierarchy), ""); |
236 | | |
237 | 14.5k | auto nextBase = it + 1; |
238 | | // Initialize the base contracts here as long as their constructors are implicit, |
239 | | // stop when the first explicit constructor is found. |
240 | 15.8k | while (nextBase != end(hierarchy)) |
241 | 1.82k | { |
242 | 1.82k | if (auto baseConstructor = (*nextBase)->constructor()) |
243 | 508 | { |
244 | 508 | createLocalVariables(*baseConstructor); |
245 | | // If any subcontract explicitly called baseConstructor, use those arguments. |
246 | 508 | if (m_baseConstructorCalls.count(*nextBase)) |
247 | 253 | inlineModifierInvocation(m_baseConstructorCalls.at(*nextBase), baseConstructor); |
248 | 255 | else if (baseConstructor->isImplemented()) |
249 | 255 | { |
250 | | // The first constructor found is handled like a function |
251 | | // and its pushed into the callstack there. |
252 | | // This if avoids duplication in the callstack. |
253 | 255 | if (!m_callStack.empty()) |
254 | 255 | pushCallStack({baseConstructor, nullptr}); |
255 | 255 | baseConstructor->accept(*this); |
256 | | // popped by endVisit(FunctionDefinition) |
257 | 255 | } |
258 | 508 | break; |
259 | 508 | } |
260 | 1.31k | else |
261 | 1.31k | { |
262 | 1.31k | initializeStateVariables(**nextBase); |
263 | 1.31k | ++nextBase; |
264 | 1.31k | } |
265 | 1.82k | } |
266 | | |
267 | 14.5k | initializeStateVariables(_contract); |
268 | 14.5k | } |
269 | | |
270 | | bool SMTEncoder::visit(PlaceholderStatement const&) |
271 | 1.10k | { |
272 | 1.10k | solAssert(!m_callStack.empty(), ""); |
273 | 1.10k | auto lastCall = popCallStack(); |
274 | 1.10k | visitFunctionOrModifier(); |
275 | 1.10k | pushCallStack(lastCall); |
276 | 1.10k | return true; |
277 | 1.10k | } |
278 | | |
279 | | void SMTEncoder::endVisit(FunctionDefinition const&) |
280 | 32.3k | { |
281 | 32.3k | solAssert(m_currentContract, ""); |
282 | | |
283 | 32.3k | popCallStack(); |
284 | 32.3k | solAssert(m_modifierDepthStack.back() == -1, ""); |
285 | 32.3k | m_modifierDepthStack.pop_back(); |
286 | 32.3k | if (m_callStack.empty()) |
287 | 30.0k | m_context.popSolver(); |
288 | 32.3k | } |
289 | | |
290 | | bool SMTEncoder::visit(Block const& _block) |
291 | 35.0k | { |
292 | 35.0k | if (_block.unchecked()) |
293 | 294 | { |
294 | 294 | solAssert(m_checked, ""); |
295 | 294 | m_checked = false; |
296 | 294 | } |
297 | 35.0k | return true; |
298 | 35.0k | } |
299 | | |
300 | | void SMTEncoder::endVisit(Block const& _block) |
301 | 35.0k | { |
302 | 35.0k | if (_block.unchecked()) |
303 | 294 | { |
304 | 294 | solAssert(!m_checked, ""); |
305 | 294 | m_checked = true; |
306 | 294 | } |
307 | 35.0k | } |
308 | | |
309 | | bool SMTEncoder::visit(InlineAssembly const& _inlineAsm) |
310 | 3.98k | { |
311 | | /// This is very similar to `yul::Assignments`, except I need to collect `Identifier`s and not just names as `YulString`s. |
312 | 3.98k | struct AssignedExternalsCollector: public yul::ASTWalker |
313 | 3.98k | { |
314 | 3.98k | AssignedExternalsCollector(InlineAssembly const& _inlineAsm): externalReferences(_inlineAsm.annotation().externalReferences) |
315 | 3.98k | { |
316 | 3.98k | this->operator()(_inlineAsm.operations().root()); |
317 | 3.98k | } |
318 | | |
319 | 3.98k | std::map<yul::Identifier const*, InlineAssemblyAnnotation::ExternalIdentifierInfo> const& externalReferences; |
320 | 3.98k | std::set<VariableDeclaration const*> assignedVars; |
321 | | |
322 | 3.98k | using yul::ASTWalker::operator(); |
323 | 3.98k | void operator()(yul::Assignment const& _assignment) |
324 | 3.98k | { |
325 | 1.64k | auto const& vars = _assignment.variableNames; |
326 | 1.64k | for (auto const& identifier: vars) |
327 | 1.65k | if (auto externalInfo = util::valueOrNullptr(externalReferences, &identifier)) |
328 | 1.34k | if (auto varDecl = dynamic_cast<VariableDeclaration const*>(externalInfo->declaration)) |
329 | 1.34k | assignedVars.insert(varDecl); |
330 | 1.64k | } |
331 | 3.98k | }; |
332 | | |
333 | 3.98k | yul::SideEffectsCollector sideEffectsCollector(_inlineAsm.dialect(), _inlineAsm.operations().root()); |
334 | 3.98k | if (sideEffectsCollector.invalidatesMemory()) |
335 | 1.08k | resetMemoryVariables(); |
336 | 3.98k | if (sideEffectsCollector.invalidatesStorage()) |
337 | 1.46k | resetStorageVariables(); |
338 | | |
339 | 3.98k | auto assignedVars = AssignedExternalsCollector(_inlineAsm).assignedVars; |
340 | 3.98k | for (auto const* var: assignedVars) |
341 | 914 | { |
342 | 914 | solAssert(var, ""); |
343 | 914 | solAssert(var->isLocalVariable(), "Non-local variable assigned in inlined assembly"); |
344 | 914 | m_context.resetVariable(*var); |
345 | 914 | } |
346 | | |
347 | 3.98k | m_unsupportedErrors.warning( |
348 | 3.98k | 7737_error, |
349 | 3.98k | _inlineAsm.location(), |
350 | 3.98k | "Inline assembly may cause SMTChecker to produce spurious warnings (false positives)." |
351 | 3.98k | ); |
352 | 3.98k | return false; |
353 | 3.98k | } |
354 | | |
355 | | void SMTEncoder::pushInlineFrame(CallableDeclaration const&) |
356 | 16.9k | { |
357 | 16.9k | pushPathCondition(currentPathConditions()); |
358 | 16.9k | } |
359 | | |
360 | | void SMTEncoder::popInlineFrame(CallableDeclaration const&) |
361 | 16.9k | { |
362 | 16.9k | popPathCondition(); |
363 | 16.9k | } |
364 | | |
365 | | void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl) |
366 | 7.99k | { |
367 | 7.99k | if (auto init = _varDecl.initialValue()) |
368 | 5.50k | expressionToTupleAssignment(_varDecl.declarations(), *init); |
369 | 7.99k | } |
370 | | |
371 | | bool SMTEncoder::visit(Assignment const& _assignment) |
372 | 19.7k | { |
373 | | // RHS must be visited before LHS; as opposed to what Assignment::accept does |
374 | 19.7k | _assignment.rightHandSide().accept(*this); |
375 | 19.7k | _assignment.leftHandSide().accept(*this); |
376 | 19.7k | return false; |
377 | 19.7k | } |
378 | | |
379 | | void SMTEncoder::endVisit(Assignment const& _assignment) |
380 | 19.7k | { |
381 | 19.7k | createExpr(_assignment); |
382 | | |
383 | 19.7k | Token op = _assignment.assignmentOperator(); |
384 | 19.7k | solAssert(TokenTraits::isAssignmentOp(op), ""); |
385 | | |
386 | 19.7k | if (!isSupportedType(*_assignment.annotation().type)) |
387 | 273 | { |
388 | | // Give it a new index anyway to keep the SSA scheme sound. |
389 | | |
390 | 273 | Expression const* base = &_assignment.leftHandSide(); |
391 | 273 | if (auto const* indexAccess = dynamic_cast<IndexAccess const*>(base)) |
392 | 42 | base = leftmostBase(*indexAccess); |
393 | | |
394 | 273 | if (auto varDecl = identifierToVariable(*base)) |
395 | 195 | m_context.newValue(*varDecl); |
396 | 273 | } |
397 | 19.5k | else |
398 | 19.5k | { |
399 | 19.5k | if (dynamic_cast<TupleType const*>(_assignment.rightHandSide().annotation().type)) |
400 | 337 | tupleAssignment(_assignment.leftHandSide(), _assignment.rightHandSide()); |
401 | 19.1k | else |
402 | 19.1k | { |
403 | 19.1k | auto const& type = _assignment.annotation().type; |
404 | 19.1k | auto rightHandSide = op == Token::Assign ? |
405 | 18.0k | expr(_assignment.rightHandSide(), type) : |
406 | 19.1k | compoundAssignment(_assignment); |
407 | 19.1k | defineExpr(_assignment, rightHandSide); |
408 | 19.1k | assignment( |
409 | 19.1k | _assignment.leftHandSide(), |
410 | 19.1k | expr(_assignment, type), |
411 | 19.1k | type |
412 | 19.1k | ); |
413 | 19.1k | } |
414 | 19.5k | } |
415 | 19.7k | } |
416 | | |
417 | | void SMTEncoder::endVisit(TupleExpression const& _tuple) |
418 | 8.86k | { |
419 | 8.86k | if (_tuple.annotation().type->category() == Type::Category::Function) |
420 | 235 | return; |
421 | | |
422 | 8.62k | if (_tuple.annotation().type->category() == Type::Category::TypeType) |
423 | 57 | return; |
424 | | |
425 | 8.56k | createExpr(_tuple); |
426 | | |
427 | 8.56k | if (_tuple.isInlineArray()) |
428 | 1.20k | { |
429 | | // Add constraints for the length and values as it is known. |
430 | 1.20k | auto symbArray = std::dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(_tuple)); |
431 | 1.20k | smtAssert(symbArray, "Inline array must be represented with SymbolicArrayVariable"); |
432 | 1.20k | auto originalType = symbArray->originalType(); |
433 | 1.20k | auto arrayType = dynamic_cast<ArrayType const*>(originalType); |
434 | 1.20k | smtAssert(arrayType, "Type of inline array must be ArrayType"); |
435 | 2.18k | addArrayLiteralAssertions(*symbArray, applyMap(_tuple.components(), [&](auto const& c) { return expr(*c, arrayType->baseType()); })); |
436 | 1.20k | } |
437 | 7.36k | else |
438 | 7.36k | { |
439 | 12.3k | auto values = applyMap(_tuple.components(), [this](auto const& component) -> std::optional<smtutil::Expression> { |
440 | 12.3k | if (component) |
441 | 12.2k | { |
442 | 12.2k | if (!m_context.knownExpression(*component)) |
443 | 12 | createExpr(*component); |
444 | 12.2k | return expr(*component); |
445 | 12.2k | } |
446 | 148 | return {}; |
447 | 12.3k | }); |
448 | 7.36k | defineExpr(_tuple, values); |
449 | 7.36k | } |
450 | 8.56k | } |
451 | | |
452 | | void SMTEncoder::endVisit(UnaryOperation const& _op) |
453 | 7.25k | { |
454 | | /// For constant expressions, we need to shortcut here |
455 | | /// due to potentially unknown rational number sizes that |
456 | | /// can appear in intermediary operations, e.g., ~(2**256 - 1). |
457 | 7.25k | if (isConstant(_op)) |
458 | 1.48k | return; |
459 | | |
460 | 5.77k | if (TokenTraits::isBitOp(_op.getOperator()) && !*_op.annotation().userDefinedFunction) |
461 | 2.15k | return bitwiseNotOperation(_op); |
462 | | |
463 | 3.62k | createExpr(_op); |
464 | | |
465 | | // User-defined operators are essentially function calls. |
466 | 3.62k | if (*_op.annotation().userDefinedFunction) |
467 | 22 | return; |
468 | | |
469 | 3.60k | auto const& subExpr = innermostTuple(_op.subExpression()); |
470 | 3.60k | auto type = _op.annotation().type; |
471 | 3.60k | switch (_op.getOperator()) |
472 | 3.60k | { |
473 | 506 | case Token::Not: // ! |
474 | 506 | { |
475 | 506 | solAssert(smt::isBool(*type), ""); |
476 | 506 | defineExpr(_op, !expr(subExpr)); |
477 | 506 | break; |
478 | 0 | } |
479 | 2.13k | case Token::Inc: // ++ (pre- or postfix) |
480 | 2.49k | case Token::Dec: // -- (pre- or postfix) |
481 | 2.49k | { |
482 | 2.49k | solAssert(smt::isInteger(*type) || smt::isFixedPoint(*type), ""); |
483 | 2.49k | solAssert(subExpr.annotation().willBeWrittenTo, ""); |
484 | 2.49k | auto innerValue = expr(subExpr); |
485 | 2.49k | auto newValue = arithmeticOperation( |
486 | 2.49k | _op.getOperator() == Token::Inc ? Token::Add : Token::Sub, |
487 | 2.49k | innerValue, |
488 | 2.49k | smtutil::Expression(size_t(1)), |
489 | 2.49k | _op.annotation().type, |
490 | 2.49k | _op |
491 | 2.49k | ).first; |
492 | 2.49k | defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue); |
493 | 2.49k | assignment(subExpr, newValue); |
494 | 2.49k | break; |
495 | 2.13k | } |
496 | 310 | case Token::Sub: // - |
497 | 310 | { |
498 | 310 | defineExpr(_op, 0 - expr(subExpr)); |
499 | 310 | break; |
500 | 2.13k | } |
501 | 294 | case Token::Delete: |
502 | 294 | { |
503 | 294 | if (auto decl = identifierToVariable(subExpr)) |
504 | 208 | { |
505 | 208 | m_context.newValue(*decl); |
506 | 208 | m_context.setZeroValue(*decl); |
507 | 208 | } |
508 | 86 | else |
509 | 86 | { |
510 | 86 | solAssert(m_context.knownExpression(subExpr), ""); |
511 | 86 | auto const& symbVar = m_context.expression(subExpr); |
512 | 86 | symbVar->increaseIndex(); |
513 | 86 | m_context.setZeroValue(*symbVar); |
514 | 86 | if ( |
515 | 86 | dynamic_cast<IndexAccess const*>(&subExpr) || |
516 | 24 | dynamic_cast<MemberAccess const*>(&subExpr) |
517 | 86 | ) |
518 | 86 | indexOrMemberAssignment(subExpr, symbVar->currentValue()); |
519 | | // Empty push added a zero value anyway, so no need to delete extra. |
520 | 0 | else if (!isEmptyPush(subExpr)) |
521 | 0 | solAssert(false, ""); |
522 | 86 | } |
523 | 294 | break; |
524 | 2.13k | } |
525 | 0 | default: |
526 | 0 | solAssert(false, ""); |
527 | 3.60k | } |
528 | 3.60k | } |
529 | | |
530 | | bool SMTEncoder::visit(UnaryOperation const& _op) |
531 | 7.25k | { |
532 | 7.25k | return !shortcutRationalNumber(_op); |
533 | 7.25k | } |
534 | | |
535 | | bool SMTEncoder::visit(BinaryOperation const& _op) |
536 | 32.7k | { |
537 | 32.7k | if (shortcutRationalNumber(_op)) |
538 | 3.68k | return false; |
539 | 29.0k | if (TokenTraits::isBooleanOp(_op.getOperator()) && !*_op.annotation().userDefinedFunction) |
540 | 519 | { |
541 | 519 | booleanOperation(_op); |
542 | 519 | return false; |
543 | 519 | } |
544 | 28.5k | return true; |
545 | 29.0k | } |
546 | | |
547 | | void SMTEncoder::endVisit(BinaryOperation const& _op) |
548 | 32.7k | { |
549 | | /// If _op is const evaluated the RationalNumber shortcut was taken. |
550 | 32.7k | if (isConstant(_op)) |
551 | 3.68k | return; |
552 | 29.0k | if (TokenTraits::isBooleanOp(_op.getOperator()) && !*_op.annotation().userDefinedFunction) |
553 | 519 | return; |
554 | | |
555 | 28.5k | createExpr(_op); |
556 | | |
557 | | // User-defined operators are essentially function calls. |
558 | 28.5k | if (*_op.annotation().userDefinedFunction) |
559 | 60 | return; |
560 | | |
561 | 28.5k | if (TokenTraits::isArithmeticOp(_op.getOperator())) |
562 | 18.2k | arithmeticOperation(_op); |
563 | 10.2k | else if (TokenTraits::isCompareOp(_op.getOperator())) |
564 | 9.44k | compareOperation(_op); |
565 | 830 | else if (TokenTraits::isBitOp(_op.getOperator()) || TokenTraits::isShiftOp(_op.getOperator())) |
566 | 830 | bitwiseOperation(_op); |
567 | 0 | else |
568 | 830 | solAssert(false, ""); |
569 | 28.5k | } |
570 | | |
571 | | bool SMTEncoder::visit(Conditional const& _op) |
572 | 1.20k | { |
573 | 1.20k | _op.condition().accept(*this); |
574 | | |
575 | 1.20k | auto indicesEndTrue = visitBranch(&_op.trueExpression(), expr(_op.condition())).first; |
576 | | |
577 | 1.20k | auto indicesEndFalse = visitBranch(&_op.falseExpression(), !expr(_op.condition())).first; |
578 | | |
579 | 1.20k | mergeVariables(expr(_op.condition()), indicesEndTrue, indicesEndFalse); |
580 | | |
581 | 1.20k | defineExpr(_op, smtutil::Expression::ite( |
582 | 1.20k | expr(_op.condition()), |
583 | 1.20k | expr(_op.trueExpression(), _op.annotation().type), |
584 | 1.20k | expr(_op.falseExpression(), _op.annotation().type) |
585 | 1.20k | )); |
586 | | |
587 | 1.20k | return false; |
588 | 1.20k | } |
589 | | |
590 | | bool SMTEncoder::visit(FunctionCall const& _funCall) |
591 | 26.9k | { |
592 | 26.9k | auto functionCallKind = *_funCall.annotation().kind; |
593 | 26.9k | if (functionCallKind != FunctionCallKind::FunctionCall) |
594 | 5.61k | return true; |
595 | | |
596 | 21.3k | FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type); |
597 | | // We do not want to visit the TypeTypes in the second argument of `abi.decode`. |
598 | | // Those types are checked/used in SymbolicState::buildABIFunctions. |
599 | 21.3k | if (funType.kind() == FunctionType::Kind::ABIDecode) |
600 | 242 | { |
601 | 242 | if (auto arg = _funCall.arguments().front()) |
602 | 242 | arg->accept(*this); |
603 | 242 | return false; |
604 | 242 | } |
605 | | |
606 | | // We do not really need to visit the expression in a wrap/unwrap no-op call, |
607 | | // so we just ignore the function call expression to avoid "unsupported" warnings. |
608 | 21.1k | else if ( |
609 | 21.1k | funType.kind() == FunctionType::Kind::Wrap || |
610 | 20.7k | funType.kind() == FunctionType::Kind::Unwrap |
611 | 21.1k | ) |
612 | 513 | { |
613 | 513 | if (auto arg = _funCall.arguments().front()) |
614 | 513 | arg->accept(*this); |
615 | 513 | return false; |
616 | 513 | } |
617 | 20.5k | return true; |
618 | 21.3k | } |
619 | | |
620 | | void SMTEncoder::endVisit(FunctionCall const& _funCall) |
621 | 25.3k | { |
622 | 25.3k | auto functionCallKind = *_funCall.annotation().kind; |
623 | | |
624 | 25.3k | createExpr(_funCall); |
625 | 25.3k | if (functionCallKind == FunctionCallKind::StructConstructorCall) |
626 | 247 | { |
627 | 247 | visitStructConstructorCall(_funCall); |
628 | 247 | return; |
629 | 247 | } |
630 | | |
631 | 25.1k | if (functionCallKind == FunctionCallKind::TypeConversion) |
632 | 5.36k | { |
633 | 5.36k | visitTypeConversion(_funCall); |
634 | 5.36k | return; |
635 | 5.36k | } |
636 | | |
637 | 19.7k | FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type); |
638 | | |
639 | 19.7k | std::vector<ASTPointer<Expression const>> const args = _funCall.arguments(); |
640 | 19.7k | switch (funType.kind()) |
641 | 19.7k | { |
642 | 4.28k | case FunctionType::Kind::Assert: |
643 | 4.28k | visitAssert(_funCall); |
644 | 4.28k | break; |
645 | 775 | case FunctionType::Kind::Require: |
646 | 775 | visitRequire(_funCall); |
647 | 775 | break; |
648 | 225 | case FunctionType::Kind::Revert: |
649 | | // Revert is a special case of require and equals to `require(false)` |
650 | 225 | addPathImpliedExpression(smtutil::Expression(false)); |
651 | 225 | break; |
652 | 66 | case FunctionType::Kind::GasLeft: |
653 | 66 | visitGasLeft(_funCall); |
654 | 66 | break; |
655 | 1.75k | case FunctionType::Kind::External: |
656 | 1.75k | if (publicGetter(_funCall.expression())) |
657 | 361 | visitPublicGetter(_funCall); |
658 | 1.75k | break; |
659 | 177 | case FunctionType::Kind::BytesConcat: |
660 | 177 | visitBytesConcat(_funCall); |
661 | 177 | break; |
662 | 242 | case FunctionType::Kind::ABIDecode: |
663 | 585 | case FunctionType::Kind::ABIEncode: |
664 | 759 | case FunctionType::Kind::ABIEncodePacked: |
665 | 803 | case FunctionType::Kind::ABIEncodeWithSelector: |
666 | 841 | case FunctionType::Kind::ABIEncodeCall: |
667 | 881 | case FunctionType::Kind::ABIEncodeWithSignature: |
668 | 881 | visitABIFunction(_funCall); |
669 | 881 | break; |
670 | 1.89k | case FunctionType::Kind::Internal: |
671 | 1.92k | case FunctionType::Kind::BareStaticCall: |
672 | 2.31k | case FunctionType::Kind::BareCall: |
673 | 2.31k | break; |
674 | 262 | case FunctionType::Kind::KECCAK256: |
675 | 302 | case FunctionType::Kind::ECRecover: |
676 | 330 | case FunctionType::Kind::SHA256: |
677 | 494 | case FunctionType::Kind::RIPEMD160: |
678 | 494 | visitCryptoFunction(_funCall); |
679 | 494 | break; |
680 | 42 | case FunctionType::Kind::BlockHash: |
681 | 42 | defineExpr(_funCall, state().blockhash(expr(*_funCall.arguments().at(0)))); |
682 | 42 | break; |
683 | 2 | case FunctionType::Kind::BlobHash: |
684 | 2 | visitBlobHash(_funCall); |
685 | 2 | break; |
686 | 75 | case FunctionType::Kind::AddMod: |
687 | 101 | case FunctionType::Kind::MulMod: |
688 | 101 | visitAddMulMod(_funCall); |
689 | 101 | break; |
690 | 190 | case FunctionType::Kind::Unwrap: |
691 | 513 | case FunctionType::Kind::Wrap: |
692 | 513 | visitWrapUnwrap(_funCall); |
693 | 513 | break; |
694 | 30 | case FunctionType::Kind::Send: |
695 | 155 | case FunctionType::Kind::Transfer: |
696 | 155 | { |
697 | 155 | auto const& memberAccess = dynamic_cast<MemberAccess const&>(_funCall.expression()); |
698 | 155 | auto const& address = memberAccess.expression(); |
699 | 155 | auto const& value = args.front(); |
700 | 155 | solAssert(value, ""); |
701 | | |
702 | 155 | smtutil::Expression thisBalance = state().balance(); |
703 | 155 | setSymbolicUnknownValue(thisBalance, TypeProvider::uint256(), m_context); |
704 | | |
705 | 155 | state().transfer(state().thisAddress(), expr(address), expr(*value)); |
706 | 155 | break; |
707 | 30 | } |
708 | 2.22k | case FunctionType::Kind::ArrayPush: |
709 | 2.22k | arrayPush(_funCall); |
710 | 2.22k | break; |
711 | 354 | case FunctionType::Kind::ArrayPop: |
712 | 354 | arrayPop(_funCall); |
713 | 354 | break; |
714 | 2.34k | case FunctionType::Kind::Event: |
715 | 2.45k | case FunctionType::Kind::Error: |
716 | | // This can be safely ignored. |
717 | 2.45k | break; |
718 | 1.95k | case FunctionType::Kind::ObjectCreation: |
719 | 1.95k | visitObjectCreation(_funCall); |
720 | 1.95k | return; |
721 | 595 | case FunctionType::Kind::Creation: |
722 | 595 | if (!m_settings.engine.chc || !m_settings.externalCalls.isTrusted()) |
723 | 595 | m_unsupportedErrors.warning( |
724 | 595 | 8729_error, |
725 | 595 | _funCall.location(), |
726 | 595 | "Contract deployment is only supported in the trusted mode for external calls" |
727 | 595 | " with the CHC engine." |
728 | 595 | ); |
729 | 595 | break; |
730 | 350 | case FunctionType::Kind::DelegateCall: |
731 | 350 | case FunctionType::Kind::BareCallCode: |
732 | 370 | case FunctionType::Kind::BareDelegateCall: |
733 | 414 | default: |
734 | 414 | m_unsupportedErrors.warning( |
735 | 414 | 4588_error, |
736 | 414 | _funCall.location(), |
737 | 414 | "Assertion checker does not yet implement this type of function call." |
738 | 414 | ); |
739 | 19.7k | } |
740 | 19.7k | } |
741 | | |
742 | | bool SMTEncoder::visit(ModifierInvocation const& _node) |
743 | 1.23k | { |
744 | 1.23k | if (auto const* args = _node.arguments()) |
745 | 691 | for (auto const& arg: *args) |
746 | 678 | if (arg) |
747 | 678 | arg->accept(*this); |
748 | 1.23k | return false; |
749 | 1.23k | } |
750 | | |
751 | | void SMTEncoder::initContract(ContractDefinition const& _contract) |
752 | 27.8k | { |
753 | 27.8k | solAssert(m_currentContract == nullptr, ""); |
754 | 27.8k | m_currentContract = &_contract; |
755 | | |
756 | 27.8k | m_context.reset(); |
757 | 27.8k | m_context.pushSolver(); |
758 | 27.8k | createStateVariables(_contract); |
759 | 27.8k | clearIndices(m_currentContract, nullptr); |
760 | 27.8k | m_checked = true; |
761 | 27.8k | } |
762 | | |
763 | | void SMTEncoder::initFunction(FunctionDefinition const& _function) |
764 | 30.0k | { |
765 | 30.0k | solAssert(m_callStack.empty(), ""); |
766 | 30.0k | solAssert(m_currentContract, ""); |
767 | 30.0k | m_context.pushSolver(); |
768 | 30.0k | m_pathConditions.clear(); |
769 | 30.0k | pushCallStack({&_function, nullptr}); |
770 | 30.0k | m_uninterpretedTerms.clear(); |
771 | 30.0k | createStateVariables(*m_currentContract); |
772 | 30.0k | createLocalVariables(_function); |
773 | 30.0k | m_arrayAssignmentHappened = false; |
774 | 30.0k | clearIndices(m_currentContract, &_function); |
775 | 30.0k | m_checked = true; |
776 | 30.0k | } |
777 | | |
778 | | void SMTEncoder::visitAssert(FunctionCall const& _funCall) |
779 | 4.28k | { |
780 | 4.28k | auto const& args = _funCall.arguments(); |
781 | 4.28k | solAssert(args.size() == 1, ""); |
782 | 4.28k | solAssert(args.front()->annotation().type->category() == Type::Category::Bool, ""); |
783 | 4.28k | } |
784 | | |
785 | | void SMTEncoder::visitRequire(FunctionCall const& _funCall) |
786 | 775 | { |
787 | 775 | auto const& args = _funCall.arguments(); |
788 | 775 | solAssert(args.size() >= 1, ""); |
789 | 775 | solAssert(args.front()->annotation().type->category() == Type::Category::Bool, ""); |
790 | 775 | addPathImpliedExpression(expr(*args.front())); |
791 | 775 | } |
792 | | |
793 | | void SMTEncoder::visitBytesConcat(FunctionCall const& _funCall) |
794 | 177 | { |
795 | 177 | auto const& args = _funCall.sortedArguments(); |
796 | | |
797 | | // bytes.concat call with no arguments returns an empty array |
798 | 177 | if (args.size() == 0) |
799 | 4 | { |
800 | 4 | defineExpr(_funCall, smt::zeroValue(TypeProvider::bytesMemory())); |
801 | 4 | return; |
802 | 4 | } |
803 | | |
804 | | // bytes.concat with single argument of type bytes memory |
805 | 173 | if (args.size() == 1 && args.front()->annotation().type->category() == frontend::Type::Category::Array) |
806 | 8 | { |
807 | 8 | defineExpr(_funCall, expr(*args.front(), TypeProvider::bytesMemory())); |
808 | 8 | return; |
809 | 8 | } |
810 | | |
811 | 165 | auto const& [name, inTypes, outType] = state().bytesConcatFunctionTypes(&_funCall); |
812 | 165 | solAssert(inTypes.size() == args.size(), ""); |
813 | | |
814 | 165 | auto symbFunction = state().bytesConcatFunction(&_funCall); |
815 | 165 | auto out = createSelectExpressionForFunction(symbFunction, args, inTypes, args.size()); |
816 | | |
817 | 165 | defineExpr(_funCall, out); |
818 | 165 | } |
819 | | |
820 | | void SMTEncoder::visitABIFunction(FunctionCall const& _funCall) |
821 | 881 | { |
822 | 881 | auto symbFunction = state().abiFunction(&_funCall); |
823 | 881 | auto const& [name, inTypes, outTypes] = state().abiFunctionTypes(&_funCall); |
824 | | |
825 | 881 | auto const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type); |
826 | 881 | auto kind = funType.kind(); |
827 | 881 | auto const& args = _funCall.sortedArguments(); |
828 | 881 | auto argsActualLength = kind == FunctionType::Kind::ABIDecode ? 1 : args.size(); |
829 | | |
830 | 881 | solAssert(inTypes.size() == argsActualLength, ""); |
831 | 881 | if (argsActualLength == 0) |
832 | 46 | { |
833 | 46 | defineExpr(_funCall, smt::zeroValue(TypeProvider::bytesMemory())); |
834 | 46 | return; |
835 | 46 | } |
836 | | |
837 | 835 | auto out = createSelectExpressionForFunction(symbFunction, args, inTypes, argsActualLength); |
838 | 835 | if (outTypes.size() == 1) |
839 | 801 | defineExpr(_funCall, out); |
840 | 34 | else |
841 | 34 | { |
842 | 34 | auto symbTuple = std::dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_funCall)); |
843 | 34 | solAssert(symbTuple, ""); |
844 | 34 | solAssert(symbTuple->components().size() == outTypes.size(), ""); |
845 | 34 | solAssert(out.sort->kind == smtutil::Kind::Tuple, ""); |
846 | | |
847 | 34 | symbTuple->increaseIndex(); |
848 | 104 | for (unsigned i = 0; i < symbTuple->components().size(); ++i) |
849 | 70 | m_context.addAssertion(symbTuple->component(i) == smtutil::Expression::tuple_get(out, i)); |
850 | 34 | } |
851 | 835 | } |
852 | | |
853 | | void SMTEncoder::visitCryptoFunction(FunctionCall const& _funCall) |
854 | 494 | { |
855 | 494 | auto const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type); |
856 | 494 | auto kind = funType.kind(); |
857 | 494 | auto arg0 = expr(*_funCall.arguments().at(0), TypeProvider::bytesStorage()); |
858 | 494 | std::optional<smtutil::Expression> result; |
859 | 494 | if (kind == FunctionType::Kind::KECCAK256) |
860 | 262 | result = smtutil::Expression::select(state().cryptoFunction("keccak256"), arg0); |
861 | 232 | else if (kind == FunctionType::Kind::SHA256) |
862 | 28 | result = smtutil::Expression::select(state().cryptoFunction("sha256"), arg0); |
863 | 204 | else if (kind == FunctionType::Kind::RIPEMD160) |
864 | 164 | result = smtutil::Expression::select(state().cryptoFunction("ripemd160"), arg0); |
865 | 40 | else if (kind == FunctionType::Kind::ECRecover) |
866 | 40 | { |
867 | 40 | auto e = state().cryptoFunction("ecrecover"); |
868 | 40 | auto arg0 = expr(*_funCall.arguments().at(0), TypeProvider::fixedBytes(32)); |
869 | 40 | auto arg1 = expr(*_funCall.arguments().at(1), TypeProvider::uint(8)); |
870 | 40 | auto arg2 = expr(*_funCall.arguments().at(2), TypeProvider::fixedBytes(32)); |
871 | 40 | auto arg3 = expr(*_funCall.arguments().at(3), TypeProvider::fixedBytes(32)); |
872 | 40 | auto inputSort = dynamic_cast<smtutil::ArraySort&>(*e.sort).domain; |
873 | 40 | auto ecrecoverInput = smtutil::Expression::tuple_constructor( |
874 | 40 | smtutil::Expression(std::make_shared<smtutil::SortSort>(inputSort), ""), |
875 | 40 | {arg0, arg1, arg2, arg3} |
876 | 40 | ); |
877 | 40 | result = smtutil::Expression::select(e, ecrecoverInput); |
878 | 40 | } |
879 | 0 | else |
880 | 40 | solAssert(false, ""); |
881 | | |
882 | 494 | defineExpr(_funCall, *result); |
883 | 494 | } |
884 | | |
885 | | void SMTEncoder::visitGasLeft(FunctionCall const& _funCall) |
886 | 66 | { |
887 | 66 | std::string gasLeft = "gasleft"; |
888 | | // We increase the variable index since gasleft changes |
889 | | // inside a tx. |
890 | 66 | defineGlobalVariable(gasLeft, _funCall, true); |
891 | 66 | auto const& symbolicVar = m_context.globalSymbol(gasLeft); |
892 | 66 | unsigned index = symbolicVar->index(); |
893 | | // We set the current value to unknown anyway to add type constraints. |
894 | 66 | m_context.setUnknownValue(*symbolicVar); |
895 | 66 | if (index > 0) |
896 | 44 | m_context.addAssertion(symbolicVar->currentValue() <= symbolicVar->valueAtIndex(index - 1)); |
897 | 66 | } |
898 | | |
899 | | void SMTEncoder::visitBlobHash(FunctionCall const& _funCall) |
900 | 2 | { |
901 | 2 | constexpr unsigned BLOB_TRANSACTION_LIMIT = 9; // Since pectra |
902 | 2 | auto indexExpr = expr(*_funCall.arguments().at(0)); |
903 | 2 | auto valueExpr = smtutil::Expression::ite( |
904 | 2 | indexExpr >= BLOB_TRANSACTION_LIMIT, |
905 | 2 | smtutil::Expression(u256(0)), |
906 | 2 | state().blobhash(indexExpr) |
907 | 2 | ); |
908 | 2 | defineExpr(_funCall, std::move(valueExpr)); |
909 | 2 | } |
910 | | |
911 | | void SMTEncoder::visitAddMulMod(FunctionCall const& _funCall) |
912 | 101 | { |
913 | 101 | auto const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type); |
914 | 101 | auto kind = funType.kind(); |
915 | 101 | solAssert(kind == FunctionType::Kind::AddMod || kind == FunctionType::Kind::MulMod, ""); |
916 | 101 | auto const& args = _funCall.arguments(); |
917 | 101 | solAssert(args.at(0) && args.at(1) && args.at(2), ""); |
918 | 101 | auto x = expr(*args.at(0)); |
919 | 101 | auto y = expr(*args.at(1)); |
920 | 101 | auto k = expr(*args.at(2)); |
921 | 101 | auto const& intType = dynamic_cast<IntegerType const&>(*_funCall.annotation().type); |
922 | | |
923 | 101 | if (kind == FunctionType::Kind::AddMod) |
924 | 75 | defineExpr(_funCall, divModWithSlacks(x + y, k, intType).second); |
925 | 26 | else |
926 | 26 | defineExpr(_funCall, divModWithSlacks(x * y, k, intType).second); |
927 | 101 | } |
928 | | |
929 | | void SMTEncoder::visitWrapUnwrap(FunctionCall const& _funCall) |
930 | 513 | { |
931 | 513 | auto const& args = _funCall.arguments(); |
932 | 513 | smtAssert(args.size() == 1, "Expected exactly one argument to wrap/unwrap"); |
933 | 513 | auto const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type); |
934 | 513 | auto const* argType = funType.parameterTypes().front(); |
935 | 513 | defineExpr(_funCall, expr(*args.front(), argType)); |
936 | 513 | } |
937 | | |
938 | | void SMTEncoder::visitObjectCreation(FunctionCall const& _funCall) |
939 | 1.95k | { |
940 | 1.95k | auto const& args = _funCall.arguments(); |
941 | 1.95k | solAssert(args.size() >= 1, ""); |
942 | 1.95k | auto argType = args.front()->annotation().type->category(); |
943 | 1.95k | solAssert(argType == Type::Category::Integer || argType == Type::Category::RationalNumber, ""); |
944 | | |
945 | 1.95k | smtutil::Expression arraySize = expr(*args.front()); |
946 | 1.95k | setSymbolicUnknownValue(arraySize, TypeProvider::uint256(), m_context); |
947 | 1.95k | auto symbArray = std::dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(_funCall)); |
948 | 1.95k | solAssert(symbArray, ""); |
949 | 1.95k | smt::setSymbolicZeroValue(*symbArray, m_context); |
950 | 1.95k | auto zeroElements = symbArray->elements(); |
951 | 1.95k | symbArray->increaseIndex(); |
952 | 1.95k | m_context.addAssertion(symbArray->length() == arraySize); |
953 | 1.95k | m_context.addAssertion(symbArray->elements() == zeroElements); |
954 | 1.95k | } |
955 | | |
956 | | void SMTEncoder::endVisit(Identifier const& _identifier) |
957 | 94.5k | { |
958 | 94.5k | if (auto decl = identifierToVariable(_identifier)) |
959 | 78.5k | { |
960 | 78.5k | if (decl->isConstant()) |
961 | 1.00k | defineExpr(_identifier, constantExpr(_identifier, *decl)); |
962 | 77.5k | else |
963 | 77.5k | defineExpr(_identifier, currentValue(*decl)); |
964 | 78.5k | } |
965 | 15.9k | else if (_identifier.annotation().type->category() == Type::Category::Function) |
966 | 11.3k | visitFunctionIdentifier(_identifier); |
967 | 4.57k | else if (_identifier.name() == "now") |
968 | 0 | defineGlobalVariable(_identifier.name(), _identifier); |
969 | 4.57k | else if (_identifier.name() == "this") |
970 | 2.30k | { |
971 | 2.30k | defineExpr(_identifier, state().thisAddress()); |
972 | 2.30k | m_uninterpretedTerms.insert(&_identifier); |
973 | 2.30k | } |
974 | | // Ignore type identifiers |
975 | 2.27k | else if (dynamic_cast<TypeType const*>(_identifier.annotation().type)) |
976 | 1.49k | return; |
977 | | // Ignore module identifiers |
978 | 780 | else if (dynamic_cast<ModuleType const*>(_identifier.annotation().type)) |
979 | 85 | return; |
980 | | // Ignore user defined value type identifiers |
981 | 695 | else if (dynamic_cast<UserDefinedValueType const*>(_identifier.annotation().type)) |
982 | 0 | return; |
983 | | // Ignore the builtin abi, it is handled in FunctionCall. |
984 | | // TODO: ignore MagicType in general (abi, block, msg, tx, type) |
985 | 695 | else if (auto magicType = dynamic_cast<MagicType const*>(_identifier.annotation().type); magicType && magicType->kind() == MagicType::Kind::ABI) |
986 | 681 | { |
987 | 681 | solAssert(_identifier.name() == "abi", ""); |
988 | 681 | return; |
989 | 681 | } |
990 | 14 | else |
991 | 14 | createExpr(_identifier); |
992 | 94.5k | } |
993 | | |
994 | | void SMTEncoder::endVisit(ElementaryTypeNameExpression const& _typeName) |
995 | 5.26k | { |
996 | 5.26k | auto const& typeType = dynamic_cast<TypeType const&>(*_typeName.annotation().type); |
997 | 5.26k | auto result = smt::newSymbolicVariable( |
998 | 5.26k | *TypeProvider::uint256(), |
999 | 5.26k | typeType.actualType()->toString(false), |
1000 | 5.26k | m_context |
1001 | 5.26k | ); |
1002 | 5.26k | solAssert(!result.first && result.second, ""); |
1003 | 5.26k | m_context.createExpression(_typeName, result.second); |
1004 | 5.26k | } |
1005 | | |
1006 | | namespace // helpers for SMTEncoder::visitPublicGetter |
1007 | | { |
1008 | | |
1009 | | bool isReturnedFromStructGetter(Type const* _type) |
1010 | 237 | { |
1011 | | // So far it seems that only Mappings and ordinary Arrays are not returned. |
1012 | 237 | auto category = _type->category(); |
1013 | 237 | if (category == Type::Category::Mapping) |
1014 | 0 | return false; |
1015 | 237 | if (category == Type::Category::Array) |
1016 | 110 | return dynamic_cast<ArrayType const&>(*_type).isByteArrayOrString(); |
1017 | | // default |
1018 | 127 | return true; |
1019 | 237 | } |
1020 | | |
1021 | | std::vector<std::string> structGetterReturnedMembers(StructType const& _structType) |
1022 | 85 | { |
1023 | 85 | std::vector<std::string> returnedMembers; |
1024 | 85 | for (auto const& member: _structType.nativeMembers(nullptr)) |
1025 | 237 | if (isReturnedFromStructGetter(member.type)) |
1026 | 186 | returnedMembers.push_back(member.name); |
1027 | 85 | return returnedMembers; |
1028 | 85 | } |
1029 | | |
1030 | | } |
1031 | | |
1032 | | void SMTEncoder::visitPublicGetter(FunctionCall const& _funCall) |
1033 | 479 | { |
1034 | 479 | auto var = publicGetter(_funCall.expression()); |
1035 | 479 | solAssert(var && var->isStateVariable(), ""); |
1036 | 479 | solAssert(m_context.knownExpression(_funCall), ""); |
1037 | 479 | auto paramExpectedTypes = replaceUserTypes(FunctionType(*var).parameterTypes()); |
1038 | 479 | auto actualArguments = _funCall.arguments(); |
1039 | 479 | solAssert(actualArguments.size() == paramExpectedTypes.size(), ""); |
1040 | 479 | std::deque<smtutil::Expression> symbArguments; |
1041 | 759 | for (unsigned i = 0; i < paramExpectedTypes.size(); ++i) |
1042 | 280 | symbArguments.push_back(expr(*actualArguments[i], paramExpectedTypes[i])); |
1043 | | |
1044 | | // See FunctionType::FunctionType(VariableDeclaration const& _varDecl) |
1045 | | // to understand the return types of public getters. |
1046 | 479 | Type const* type = var->type(); |
1047 | 479 | smtutil::Expression currentExpr = currentValue(*var); |
1048 | 759 | while (true) |
1049 | 759 | { |
1050 | 759 | if ( |
1051 | 759 | type->isValueType() || |
1052 | 374 | (type->category() == Type::Category::Array && dynamic_cast<ArrayType const&>(*type).isByteArrayOrString()) |
1053 | 759 | ) |
1054 | 394 | { |
1055 | 394 | solAssert(symbArguments.empty(), ""); |
1056 | 394 | defineExpr(_funCall, currentExpr); |
1057 | 394 | return; |
1058 | 394 | } |
1059 | 365 | switch (type->category()) |
1060 | 365 | { |
1061 | 216 | case Type::Category::Array: |
1062 | 280 | case Type::Category::Mapping: |
1063 | 280 | { |
1064 | 280 | solAssert(!symbArguments.empty(), ""); |
1065 | | // For nested arrays/mappings, each argument in the call is an index to the next layer. |
1066 | | // We mirror this with `select` after unpacking the SMT-LIB array expression. |
1067 | 280 | currentExpr = smtutil::Expression::select(smtutil::Expression::tuple_get(currentExpr, 0), symbArguments.front()); |
1068 | 280 | symbArguments.pop_front(); |
1069 | 280 | if (auto arrayType = dynamic_cast<ArrayType const*>(type)) |
1070 | 216 | type = arrayType->baseType(); |
1071 | 64 | else if (auto mappingType = dynamic_cast<MappingType const*>(type)) |
1072 | 64 | type = mappingType->valueType(); |
1073 | 0 | else |
1074 | 64 | solAssert(false, ""); |
1075 | 280 | break; |
1076 | 216 | } |
1077 | 85 | case Type::Category::Struct: |
1078 | 85 | { |
1079 | 85 | solAssert(symbArguments.empty(), ""); |
1080 | 85 | smt::SymbolicStructVariable structVar(dynamic_cast<StructType const*>(type), "struct_temp_" + std::to_string(_funCall.id()), m_context); |
1081 | 85 | m_context.addAssertion(structVar.currentValue() == currentExpr); |
1082 | 85 | auto returnedMembers = structGetterReturnedMembers(dynamic_cast<StructType const&>(*structVar.type())); |
1083 | 85 | solAssert(!returnedMembers.empty(), ""); |
1084 | 186 | auto returnedValues = applyMap(returnedMembers, [&](std::string const& memberName) -> std::optional<smtutil::Expression> { return structVar.member(memberName); }); |
1085 | 85 | defineExpr(_funCall, returnedValues); |
1086 | 85 | return; |
1087 | 216 | } |
1088 | 0 | default: |
1089 | 0 | { |
1090 | | // Unsupported cases, do nothing and the getter will be abstracted. |
1091 | 0 | return; |
1092 | 216 | } |
1093 | 365 | } |
1094 | 365 | } |
1095 | 479 | } |
1096 | | |
1097 | | bool SMTEncoder::shouldEncode(ContractDefinition const& _contract) const |
1098 | 83.9k | { |
1099 | 83.9k | return _contract.canBeDeployed(); |
1100 | 83.9k | } |
1101 | | |
1102 | | bool SMTEncoder::shouldAnalyzeVerificationTargetsFor(SourceUnit const& _source) const |
1103 | 13.4k | { |
1104 | 13.4k | return m_settings.contracts.isDefault() || |
1105 | 0 | m_settings.contracts.has(*_source.annotation().path); |
1106 | 13.4k | } |
1107 | | |
1108 | | bool SMTEncoder::shouldAnalyzeVerificationTargetsFor(ContractDefinition const& _contract) const |
1109 | 28.2k | { |
1110 | 28.2k | if (!shouldEncode(_contract)) |
1111 | 7 | return false; |
1112 | | |
1113 | 28.2k | return m_settings.contracts.isDefault() || |
1114 | 0 | m_settings.contracts.has(_contract.sourceUnitName(), _contract.name()); |
1115 | 28.2k | } |
1116 | | |
1117 | | void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall) |
1118 | 5.36k | { |
1119 | 5.36k | solAssert(*_funCall.annotation().kind == FunctionCallKind::TypeConversion, ""); |
1120 | 5.36k | solAssert(_funCall.arguments().size() == 1, ""); |
1121 | | |
1122 | 5.36k | auto argument = _funCall.arguments().front(); |
1123 | 5.36k | auto const argType = argument->annotation().type; |
1124 | 5.36k | auto const funCallType = _funCall.annotation().type; |
1125 | | |
1126 | 5.36k | auto symbArg = expr(*argument, funCallType); |
1127 | | |
1128 | 5.36k | if (smt::isStringLiteral(*argType) && smt::isFixedBytes(*funCallType)) |
1129 | 114 | { |
1130 | 114 | defineExpr(_funCall, symbArg); |
1131 | 114 | return; |
1132 | 114 | } |
1133 | | |
1134 | 5.24k | ArrayType const* arrayType = dynamic_cast<ArrayType const*>(argType); |
1135 | 5.24k | if (auto sliceType = dynamic_cast<ArraySliceType const*>(argType)) |
1136 | 185 | arrayType = &sliceType->arrayType(); |
1137 | | |
1138 | 5.24k | if (arrayType && arrayType->isByteArrayOrString() && smt::isFixedBytes(*funCallType)) |
1139 | 126 | { |
1140 | 126 | auto array = std::dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(*argument)); |
1141 | 126 | bytesToFixedBytesAssertions(*array, _funCall); |
1142 | 126 | return; |
1143 | 126 | } |
1144 | | |
1145 | | // TODO Simplify this whole thing for 0.8.0 where weird casts are disallowed. |
1146 | | |
1147 | 5.12k | unsigned argSize = argType->storageBytes(); |
1148 | 5.12k | unsigned castSize = funCallType->storageBytes(); |
1149 | 5.12k | bool castIsSigned = smt::isNumber(*funCallType) && smt::isSigned(funCallType); |
1150 | 5.12k | bool argIsSigned = smt::isNumber(*argType) && smt::isSigned(argType); |
1151 | 5.12k | std::optional<smtutil::Expression> symbMin; |
1152 | 5.12k | std::optional<smtutil::Expression> symbMax; |
1153 | 5.12k | if (smt::isNumber(*funCallType)) |
1154 | 4.78k | { |
1155 | 4.78k | symbMin = smt::minValue(funCallType); |
1156 | 4.78k | symbMax = smt::maxValue(funCallType); |
1157 | 4.78k | } |
1158 | 5.12k | if (argSize == castSize) |
1159 | 2.83k | { |
1160 | | // If sizes are the same, it's possible that the signs are different. |
1161 | 2.83k | if (smt::isNumber(*funCallType) && smt::isNumber(*argType)) |
1162 | 2.49k | { |
1163 | | // castIsSigned && !argIsSigned => might overflow if arg > castType.max |
1164 | | // !castIsSigned && argIsSigned => might underflow if arg < castType.min |
1165 | | // !castIsSigned && !argIsSigned => ok |
1166 | | // castIsSigned && argIsSigned => ok |
1167 | | |
1168 | 2.49k | if (castIsSigned && !argIsSigned) |
1169 | 146 | { |
1170 | 146 | auto wrap = smtutil::Expression::ite( |
1171 | 146 | symbArg > *symbMax, |
1172 | 146 | symbArg - (*symbMax - *symbMin + 1), |
1173 | 146 | symbArg |
1174 | 146 | ); |
1175 | 146 | defineExpr(_funCall, wrap); |
1176 | 146 | } |
1177 | 2.34k | else if (!castIsSigned && argIsSigned) |
1178 | 48 | { |
1179 | 48 | auto wrap = smtutil::Expression::ite( |
1180 | 48 | symbArg < *symbMin, |
1181 | 48 | symbArg + (*symbMax + 1), |
1182 | 48 | symbArg |
1183 | 48 | ); |
1184 | 48 | defineExpr(_funCall, wrap); |
1185 | 48 | } |
1186 | 2.29k | else |
1187 | 2.29k | defineExpr(_funCall, symbArg); |
1188 | 2.49k | } |
1189 | 341 | else |
1190 | 341 | defineExpr(_funCall, symbArg); |
1191 | 2.83k | } |
1192 | 2.29k | else if (castSize > argSize) |
1193 | 207 | { |
1194 | 207 | solAssert(smt::isNumber(*funCallType), ""); |
1195 | | // RationalNumbers have size 32. |
1196 | 207 | solAssert(argType->category() != Type::Category::RationalNumber, ""); |
1197 | | |
1198 | | // castIsSigned && !argIsSigned => ok |
1199 | | // castIsSigned && argIsSigned => ok |
1200 | | // !castIsSigned && !argIsSigned => ok except for FixedBytesType, need to adjust padding |
1201 | | // !castIsSigned && argIsSigned => might underflow if arg < castType.min |
1202 | | |
1203 | 207 | if (!castIsSigned && argIsSigned) |
1204 | 0 | { |
1205 | 0 | auto wrap = smtutil::Expression::ite( |
1206 | 0 | symbArg < *symbMin, |
1207 | 0 | symbArg + (*symbMax + 1), |
1208 | 0 | symbArg |
1209 | 0 | ); |
1210 | 0 | defineExpr(_funCall, wrap); |
1211 | 0 | } |
1212 | 207 | else if (!castIsSigned && !argIsSigned) |
1213 | 183 | { |
1214 | 183 | if (auto const* fixedCast = dynamic_cast<FixedBytesType const*>(funCallType)) |
1215 | 96 | { |
1216 | 96 | auto const* fixedArg = dynamic_cast<FixedBytesType const*>(argType); |
1217 | 96 | solAssert(fixedArg, ""); |
1218 | 96 | auto diff = fixedCast->numBytes() - fixedArg->numBytes(); |
1219 | 96 | solAssert(diff > 0, ""); |
1220 | 96 | auto bvSize = fixedCast->numBytes() * 8; |
1221 | 96 | defineExpr( |
1222 | 96 | _funCall, |
1223 | 96 | smtutil::Expression::bv2int(smtutil::Expression::int2bv(symbArg, bvSize) << smtutil::Expression::int2bv(diff * 8, bvSize)) |
1224 | 96 | ); |
1225 | 96 | } |
1226 | 87 | else |
1227 | 87 | defineExpr(_funCall, symbArg); |
1228 | 183 | } |
1229 | 24 | else |
1230 | 24 | defineExpr(_funCall, symbArg); |
1231 | 207 | } |
1232 | 2.08k | else // castSize < argSize |
1233 | 2.08k | { |
1234 | 2.08k | solAssert(smt::isNumber(*funCallType), ""); |
1235 | | |
1236 | 2.08k | RationalNumberType const* rationalType = isConstant(*argument); |
1237 | 2.08k | if (rationalType) |
1238 | 1.89k | { |
1239 | | // The TypeChecker guarantees that a constant fits in the cast size. |
1240 | 1.89k | defineExpr(_funCall, symbArg); |
1241 | 1.89k | return; |
1242 | 1.89k | } |
1243 | | |
1244 | 189 | auto const* fixedCast = dynamic_cast<FixedBytesType const*>(funCallType); |
1245 | 189 | auto const* fixedArg = dynamic_cast<FixedBytesType const*>(argType); |
1246 | 189 | if (fixedCast && fixedArg) |
1247 | 36 | { |
1248 | 36 | createExpr(_funCall); |
1249 | 36 | auto diff = argSize - castSize; |
1250 | 36 | solAssert(fixedArg->numBytes() - fixedCast->numBytes() == diff, ""); |
1251 | | |
1252 | 36 | auto argValueBV = smtutil::Expression::int2bv(symbArg, argSize * 8); |
1253 | 36 | auto shr = smtutil::Expression::int2bv(diff * 8, argSize * 8); |
1254 | 36 | solAssert(!castIsSigned, ""); |
1255 | 36 | defineExpr(_funCall, smtutil::Expression::bv2int(argValueBV >> shr)); |
1256 | 36 | } |
1257 | 153 | else |
1258 | 153 | { |
1259 | 153 | auto argValueBV = smtutil::Expression::int2bv(symbArg, castSize * 8); |
1260 | 153 | defineExpr(_funCall, smtutil::Expression::bv2int(argValueBV, castIsSigned)); |
1261 | 153 | } |
1262 | 189 | } |
1263 | 5.12k | } |
1264 | | |
1265 | | void SMTEncoder::visitFunctionIdentifier(Identifier const& _identifier) |
1266 | 11.3k | { |
1267 | 11.3k | auto const& fType = dynamic_cast<FunctionType const&>(*_identifier.annotation().type); |
1268 | 11.3k | if (replaceUserTypes(fType.returnParameterTypes()).size() == 1) |
1269 | 2.69k | { |
1270 | 2.69k | defineGlobalVariable(fType.identifier(), _identifier); |
1271 | 2.69k | m_context.createExpression(_identifier, m_context.globalSymbol(fType.identifier())); |
1272 | 2.69k | } |
1273 | 11.3k | } |
1274 | | |
1275 | | void SMTEncoder::visitStructConstructorCall(FunctionCall const& _funCall) |
1276 | 247 | { |
1277 | 247 | solAssert(*_funCall.annotation().kind == FunctionCallKind::StructConstructorCall, ""); |
1278 | 247 | if (smt::isNonRecursiveStruct(*_funCall.annotation().type)) |
1279 | 243 | { |
1280 | 243 | auto& structSymbolicVar = dynamic_cast<smt::SymbolicStructVariable&>(*m_context.expression(_funCall)); |
1281 | 243 | auto structType = dynamic_cast<StructType const*>(structSymbolicVar.type()); |
1282 | 243 | solAssert(structType, ""); |
1283 | 243 | auto const& structMembers = structType->structDefinition().members(); |
1284 | 243 | solAssert(structMembers.size() == _funCall.sortedArguments().size(), ""); |
1285 | 243 | auto args = _funCall.sortedArguments(); |
1286 | 243 | structSymbolicVar.assignAllMembers(applyMap( |
1287 | 243 | ranges::views::zip(args, structMembers), |
1288 | 413 | [this] (auto const& argMemberPair) { return expr(*argMemberPair.first, argMemberPair.second->type()); } |
1289 | 243 | )); |
1290 | 243 | } |
1291 | | |
1292 | 247 | } |
1293 | | |
1294 | | void SMTEncoder::endVisit(Literal const& _literal) |
1295 | 78.7k | { |
1296 | 78.7k | solAssert(_literal.annotation().type, "Expected type for AST node"); |
1297 | 78.7k | Type const& type = *_literal.annotation().type; |
1298 | 78.7k | if (smt::isNumber(type)) |
1299 | 63.7k | defineExpr(_literal, smtutil::Expression(type.literalValue(&_literal))); |
1300 | 14.9k | else if (smt::isBool(type)) |
1301 | 2.10k | defineExpr(_literal, smtutil::Expression(_literal.token() == Token::TrueLiteral ? true : false)); |
1302 | 12.8k | else if (smt::isStringLiteral(type)) |
1303 | 12.8k | { |
1304 | 12.8k | createExpr(_literal); |
1305 | | |
1306 | | // Add constraints for the length and values as it is known. |
1307 | 12.8k | auto symbArray = std::dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(_literal)); |
1308 | 12.8k | solAssert(symbArray, ""); |
1309 | | |
1310 | 12.8k | addArrayLiteralAssertions( |
1311 | 12.8k | *symbArray, |
1312 | 267k | applyMap(_literal.value(), [](unsigned char c) { return smtutil::Expression{size_t(c)}; }) |
1313 | 12.8k | ); |
1314 | 12.8k | } |
1315 | 0 | else |
1316 | 12.8k | solAssert(false, ""); |
1317 | 78.7k | } |
1318 | | |
1319 | | void SMTEncoder::addArrayLiteralAssertions( |
1320 | | smt::SymbolicArrayVariable& _symArray, |
1321 | | std::vector<smtutil::Expression> const& _elementValues |
1322 | | ) |
1323 | 14.0k | { |
1324 | 14.0k | m_context.addAssertion(_symArray.length() == _elementValues.size()); |
1325 | | |
1326 | | // Assert to the solver that _elementValues are exactly the elements at the beginning of the array. |
1327 | | // Since we create new symbolic representation for every array literal in the source file, we want to ensure that |
1328 | | // representations of two equal literals are also equal. For this reason we always start from constant-zero array. |
1329 | | // This ensures SMT-LIB arrays (which are infinite) are also equal beyond the length of the Solidity array literal. |
1330 | 14.0k | auto type = _symArray.type(); |
1331 | 14.0k | smtAssert(type); |
1332 | 14.0k | auto valueType = [&]() { |
1333 | 14.0k | if (auto const* arrayType = dynamic_cast<ArrayType const*>(type)) |
1334 | 1.20k | return arrayType->baseType(); |
1335 | 12.8k | if (smt::isStringLiteral(*type)) |
1336 | 12.8k | return TypeProvider::stringMemory()->baseType(); |
1337 | 0 | smtAssert(false); |
1338 | 0 | }(); |
1339 | 14.0k | auto tupleSort = std::dynamic_pointer_cast<smtutil::TupleSort>(smt::smtSort(*type)); |
1340 | 14.0k | auto sortSort = std::make_shared<smtutil::SortSort>(tupleSort->components.front()); |
1341 | 14.0k | smtutil::Expression arrayExpr = smtutil::Expression::const_array(smtutil::Expression(sortSort), smt::zeroValue(valueType)); |
1342 | 14.0k | smtAssert(arrayExpr.sort->kind == smtutil::Kind::Array); |
1343 | 284k | for (size_t i = 0; i < _elementValues.size(); i++) |
1344 | 269k | arrayExpr = smtutil::Expression::store(arrayExpr, smtutil::Expression(i), _elementValues[i]); |
1345 | 14.0k | m_context.addAssertion(_symArray.elements() == arrayExpr); |
1346 | 14.0k | } |
1347 | | |
1348 | | void SMTEncoder::bytesToFixedBytesAssertions( |
1349 | | smt::SymbolicArrayVariable& _symArray, |
1350 | | Expression const& _fixedBytes |
1351 | | ) |
1352 | 126 | { |
1353 | 126 | auto const& fixed = dynamic_cast<FixedBytesType const&>(*_fixedBytes.annotation().type); |
1354 | 126 | auto intType = TypeProvider::uint256(); |
1355 | 126 | std::string suffix = std::to_string(_fixedBytes.id()) + "_" + std::to_string(m_context.newUniqueId()); |
1356 | 126 | smt::SymbolicIntVariable k(intType, intType, "k_" + suffix, m_context); |
1357 | 126 | m_context.addAssertion(k.currentValue() == 0); |
1358 | 126 | size_t n = fixed.numBytes(); |
1359 | 1.24k | for (size_t i = 0; i < n; i++) |
1360 | 1.11k | { |
1361 | 1.11k | auto kPrev = k.currentValue(); |
1362 | 1.11k | m_context.addAssertion((smtutil::Expression::select(_symArray.elements(), i) * (u256(1) << ((n - i - 1) * 8))) + kPrev == k.increaseIndex()); |
1363 | 1.11k | } |
1364 | 126 | m_context.addAssertion(expr(_fixedBytes) == k.currentValue()); |
1365 | 126 | } |
1366 | | |
1367 | | void SMTEncoder::endVisit(Return const& _return) |
1368 | 10.7k | { |
1369 | 10.7k | if (_return.expression() && m_context.knownExpression(*_return.expression())) |
1370 | 9.96k | { |
1371 | 9.96k | auto returnParams = m_callStack.back().first->returnParameters(); |
1372 | 9.96k | if (returnParams.size() > 1) |
1373 | 1.57k | { |
1374 | 1.57k | auto const& symbTuple = std::dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(*_return.expression())); |
1375 | 1.57k | solAssert(symbTuple, ""); |
1376 | 1.57k | solAssert(symbTuple->components().size() == returnParams.size(), ""); |
1377 | | |
1378 | 1.57k | auto const* tupleType = dynamic_cast<TupleType const*>(_return.expression()->annotation().type); |
1379 | 1.57k | solAssert(tupleType, ""); |
1380 | 1.57k | auto const& types = tupleType->components(); |
1381 | 1.57k | solAssert(types.size() == returnParams.size(), ""); |
1382 | | |
1383 | 5.50k | for (unsigned i = 0; i < returnParams.size(); ++i) |
1384 | 3.92k | assignment(*returnParams.at(i), symbTuple->component(i, types.at(i), returnParams.at(i)->type())); |
1385 | 1.57k | } |
1386 | 8.38k | else if (returnParams.size() == 1) |
1387 | 8.38k | assignment(*returnParams.front(), expr(*_return.expression(), returnParams.front()->type())); |
1388 | 9.96k | } |
1389 | 10.7k | } |
1390 | | |
1391 | | bool SMTEncoder::visit(MemberAccess const& _memberAccess) |
1392 | 14.6k | { |
1393 | 14.6k | createExpr(_memberAccess); |
1394 | | |
1395 | 14.6k | auto const& accessType = _memberAccess.annotation().type; |
1396 | 14.6k | if (accessType->category() == Type::Category::Function) |
1397 | 7.52k | { |
1398 | 7.52k | auto const* functionType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type); |
1399 | 7.52k | if (functionType && functionType->hasDeclaration()) |
1400 | 3.27k | defineExpr( |
1401 | 3.27k | _memberAccess, |
1402 | 3.27k | util::selectorFromSignatureU32(functionType->richIdentifier()) |
1403 | 3.27k | ); |
1404 | | |
1405 | 7.52k | return true; |
1406 | 7.52k | } |
1407 | | |
1408 | 7.12k | Expression const& memberExpr = innermostTuple(_memberAccess.expression()); |
1409 | | |
1410 | 7.12k | auto const& exprType = memberExpr.annotation().type; |
1411 | 7.12k | solAssert(exprType, ""); |
1412 | | |
1413 | 7.12k | if (exprType->category() == Type::Category::Magic) |
1414 | 1.19k | { |
1415 | 1.19k | if (auto const* identifier = dynamic_cast<Identifier const*>(&memberExpr)) |
1416 | 857 | { |
1417 | 857 | auto const& name = identifier->name(); |
1418 | 857 | solAssert(name == "block" || name == "msg" || name == "tx", ""); |
1419 | 857 | auto memberName = _memberAccess.memberName(); |
1420 | | |
1421 | | // TODO remove this for 0.9.0 |
1422 | 857 | if (name == "block" && memberName == "difficulty") |
1423 | 16 | memberName = "prevrandao"; |
1424 | | |
1425 | 857 | defineExpr(_memberAccess, state().txMember(name + "." + memberName)); |
1426 | 857 | } |
1427 | 337 | else if (auto magicType = dynamic_cast<MagicType const*>(exprType)) |
1428 | 337 | { |
1429 | 337 | if (magicType->kind() == MagicType::Kind::Block) |
1430 | 0 | defineExpr(_memberAccess, state().txMember("block." + _memberAccess.memberName())); |
1431 | 337 | else if (magicType->kind() == MagicType::Kind::Message) |
1432 | 0 | defineExpr(_memberAccess, state().txMember("msg." + _memberAccess.memberName())); |
1433 | 337 | else if (magicType->kind() == MagicType::Kind::Transaction) |
1434 | 0 | defineExpr(_memberAccess, state().txMember("tx." + _memberAccess.memberName())); |
1435 | 337 | else if (magicType->kind() == MagicType::Kind::MetaType) |
1436 | 337 | { |
1437 | 337 | auto const& memberName = _memberAccess.memberName(); |
1438 | 337 | if (memberName == "min" || memberName == "max") |
1439 | 257 | { |
1440 | 257 | if (IntegerType const* integerType = dynamic_cast<IntegerType const*>(magicType->typeArgument())) |
1441 | 247 | defineExpr(_memberAccess, memberName == "min" ? integerType->minValue() : integerType->maxValue()); |
1442 | 10 | else if (EnumType const* enumType = dynamic_cast<EnumType const*>(magicType->typeArgument())) |
1443 | 10 | defineExpr(_memberAccess, memberName == "min" ? enumType->minValue() : enumType->maxValue()); |
1444 | 257 | } |
1445 | 80 | else if (memberName == "interfaceId") |
1446 | 12 | { |
1447 | 12 | ContractDefinition const& contract = dynamic_cast<ContractType const&>(*magicType->typeArgument()).contractDefinition(); |
1448 | 12 | defineExpr(_memberAccess, contract.interfaceId()); |
1449 | 12 | } |
1450 | 68 | else |
1451 | | // NOTE: supporting name, creationCode, runtimeCode would be easy enough, but the bytes/string they return are not |
1452 | | // at all usable in the SMT checker currently |
1453 | 68 | m_unsupportedErrors.warning( |
1454 | 68 | 7507_error, |
1455 | 68 | _memberAccess.location(), |
1456 | 68 | "Assertion checker does not yet support this expression." |
1457 | 68 | ); |
1458 | | |
1459 | 337 | } |
1460 | 337 | } |
1461 | 0 | else |
1462 | 337 | solAssert(false, ""); |
1463 | | |
1464 | 1.19k | return false; |
1465 | 1.19k | } |
1466 | 5.92k | else if (smt::isNonRecursiveStruct(*exprType)) |
1467 | 3.36k | { |
1468 | 3.36k | memberExpr.accept(*this); |
1469 | 3.36k | auto const& symbStruct = std::dynamic_pointer_cast<smt::SymbolicStructVariable>(m_context.expression(memberExpr)); |
1470 | 3.36k | defineExpr(_memberAccess, symbStruct->member(_memberAccess.memberName())); |
1471 | 3.36k | return false; |
1472 | 3.36k | } |
1473 | 2.56k | else if (exprType->category() == Type::Category::TypeType) |
1474 | 269 | { |
1475 | 269 | auto const* decl = expressionToDeclaration(memberExpr); |
1476 | 269 | if (dynamic_cast<EnumDefinition const*>(decl)) |
1477 | 160 | { |
1478 | 160 | auto enumType = dynamic_cast<EnumType const*>(accessType); |
1479 | 160 | solAssert(enumType, ""); |
1480 | 160 | defineExpr(_memberAccess, enumType->memberValue(_memberAccess.memberName())); |
1481 | | |
1482 | 160 | return false; |
1483 | 160 | } |
1484 | 109 | else if (dynamic_cast<ContractDefinition const*>(decl)) |
1485 | 109 | { |
1486 | 109 | if (auto const* var = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration)) |
1487 | 81 | { |
1488 | 81 | if (var->isConstant()) |
1489 | 16 | defineExpr(_memberAccess, constantExpr(_memberAccess, *var)); |
1490 | 65 | else |
1491 | 65 | defineExpr(_memberAccess, currentValue(*var)); |
1492 | 81 | return false; |
1493 | 81 | } |
1494 | 109 | } |
1495 | 269 | } |
1496 | 2.30k | else if (exprType->category() == Type::Category::Address) |
1497 | 253 | { |
1498 | 253 | memberExpr.accept(*this); |
1499 | 253 | if (_memberAccess.memberName() == "balance") |
1500 | 185 | { |
1501 | 185 | defineExpr(_memberAccess, state().balance(expr(memberExpr))); |
1502 | 185 | setSymbolicUnknownValue(*m_context.expression(_memberAccess), m_context); |
1503 | 185 | m_uninterpretedTerms.insert(&_memberAccess); |
1504 | 185 | return false; |
1505 | 185 | } |
1506 | 253 | } |
1507 | 2.04k | else if (exprType->category() == Type::Category::Array) |
1508 | 1.52k | { |
1509 | 1.52k | memberExpr.accept(*this); |
1510 | 1.52k | if (_memberAccess.memberName() == "length") |
1511 | 1.52k | { |
1512 | 1.52k | auto symbArray = std::dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(memberExpr)); |
1513 | 1.52k | solAssert(symbArray, ""); |
1514 | 1.52k | defineExpr(_memberAccess, symbArray->length()); |
1515 | 1.52k | m_uninterpretedTerms.insert(&_memberAccess); |
1516 | 1.52k | setSymbolicUnknownValue( |
1517 | 1.52k | expr(_memberAccess), |
1518 | 1.52k | _memberAccess.annotation().type, |
1519 | 1.52k | m_context |
1520 | 1.52k | ); |
1521 | 1.52k | } |
1522 | 1.52k | return false; |
1523 | 1.52k | } |
1524 | 526 | else if ( |
1525 | 526 | auto const* functionType = dynamic_cast<FunctionType const*>(exprType); |
1526 | 526 | functionType && |
1527 | 422 | _memberAccess.memberName() == "selector" && |
1528 | 324 | functionType->hasDeclaration() |
1529 | 526 | ) |
1530 | 298 | { |
1531 | 298 | defineExpr(_memberAccess, functionType->externalIdentifier()); |
1532 | 298 | return false; |
1533 | 298 | } |
1534 | 228 | else if (exprType->category() == Type::Category::Module) |
1535 | 70 | { |
1536 | 70 | if (auto const* var = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration)) |
1537 | 22 | { |
1538 | 22 | solAssert(var->isConstant(), ""); |
1539 | 22 | defineExpr(_memberAccess, constantExpr(_memberAccess, *var)); |
1540 | 22 | return false; |
1541 | 22 | } |
1542 | 70 | } |
1543 | | |
1544 | 302 | m_unsupportedErrors.warning( |
1545 | 302 | 7650_error, |
1546 | 302 | _memberAccess.location(), |
1547 | 302 | "Assertion checker does not yet support this expression." |
1548 | 302 | ); |
1549 | | |
1550 | 302 | return true; |
1551 | 7.12k | } |
1552 | | |
1553 | | void SMTEncoder::endVisit(IndexAccess const& _indexAccess) |
1554 | 27.5k | { |
1555 | 27.5k | createExpr(_indexAccess); |
1556 | | |
1557 | 27.5k | if (_indexAccess.annotation().type->category() == Type::Category::TypeType) |
1558 | 50 | return; |
1559 | | |
1560 | 27.5k | makeOutOfBoundsVerificationTarget(_indexAccess); |
1561 | | |
1562 | 27.5k | if (auto const* type = dynamic_cast<FixedBytesType const*>(_indexAccess.baseExpression().annotation().type)) |
1563 | 992 | { |
1564 | 992 | smtutil::Expression base = expr(_indexAccess.baseExpression()); |
1565 | | |
1566 | 992 | if (type->numBytes() == 1) |
1567 | 876 | defineExpr(_indexAccess, base); |
1568 | 116 | else |
1569 | 116 | { |
1570 | 116 | auto [bvSize, isSigned] = smt::typeBvSizeAndSignedness(_indexAccess.baseExpression().annotation().type); |
1571 | 116 | solAssert(!isSigned, ""); |
1572 | 116 | solAssert(bvSize >= 16, ""); |
1573 | 116 | solAssert(bvSize % 8 == 0, ""); |
1574 | | |
1575 | 116 | smtutil::Expression idx = expr(*_indexAccess.indexExpression()); |
1576 | | |
1577 | 116 | auto bvBase = smtutil::Expression::int2bv(base, bvSize); |
1578 | 116 | auto bvShl = smtutil::Expression::int2bv(idx * 8, bvSize); |
1579 | 116 | auto bvShr = smtutil::Expression::int2bv(bvSize - 8, bvSize); |
1580 | 116 | auto result = (bvBase << bvShl) >> bvShr; |
1581 | | |
1582 | 116 | auto anyValue = expr(_indexAccess); |
1583 | 116 | m_context.expression(_indexAccess)->increaseIndex(); |
1584 | 116 | unsigned numBytes = bvSize / 8; |
1585 | 116 | auto withBound = smtutil::Expression::ite( |
1586 | 116 | idx < numBytes, |
1587 | 116 | smtutil::Expression::bv2int(result, false), |
1588 | 116 | anyValue |
1589 | 116 | ); |
1590 | 116 | defineExpr(_indexAccess, withBound); |
1591 | 116 | } |
1592 | 992 | return; |
1593 | 992 | } |
1594 | | |
1595 | 26.5k | std::shared_ptr<smt::SymbolicVariable> array; |
1596 | 26.5k | if (auto const* id = dynamic_cast<Identifier const*>(&_indexAccess.baseExpression())) |
1597 | 15.4k | { |
1598 | 15.4k | auto varDecl = identifierToVariable(*id); |
1599 | 15.4k | solAssert(varDecl, ""); |
1600 | 15.4k | array = m_context.variable(*varDecl); |
1601 | | |
1602 | 15.4k | if (varDecl && varDecl->isConstant()) |
1603 | 0 | m_context.addAssertion(currentValue(*varDecl) == expr(*id)); |
1604 | 15.4k | } |
1605 | 11.0k | else |
1606 | 11.0k | { |
1607 | 11.0k | solAssert(m_context.knownExpression(_indexAccess.baseExpression()), ""); |
1608 | 11.0k | array = m_context.expression(_indexAccess.baseExpression()); |
1609 | 11.0k | } |
1610 | | |
1611 | 26.5k | auto arrayVar = std::dynamic_pointer_cast<smt::SymbolicArrayVariable>(array); |
1612 | 26.5k | solAssert(arrayVar, ""); |
1613 | | |
1614 | 26.5k | Type const* baseType = _indexAccess.baseExpression().annotation().type; |
1615 | 26.5k | defineExpr(_indexAccess, smtutil::Expression::select( |
1616 | 26.5k | arrayVar->elements(), |
1617 | 26.5k | expr(*_indexAccess.indexExpression(), keyType(baseType)) |
1618 | 26.5k | )); |
1619 | 26.5k | setSymbolicUnknownValue( |
1620 | 26.5k | expr(_indexAccess), |
1621 | 26.5k | _indexAccess.annotation().type, |
1622 | 26.5k | m_context |
1623 | 26.5k | ); |
1624 | 26.5k | m_uninterpretedTerms.insert(&_indexAccess); |
1625 | 26.5k | } |
1626 | | |
1627 | | void SMTEncoder::endVisit(IndexRangeAccess const& _indexRangeAccess) |
1628 | 430 | { |
1629 | 430 | createExpr(_indexRangeAccess); |
1630 | | /// The actual slice is created by CHC which also assigns the length. |
1631 | 430 | } |
1632 | | |
1633 | | void SMTEncoder::arrayAssignment() |
1634 | 1.39k | { |
1635 | 1.39k | m_arrayAssignmentHappened = true; |
1636 | 1.39k | } |
1637 | | |
1638 | | void SMTEncoder::indexOrMemberAssignment(Expression const& _expr, smtutil::Expression const& _rightHandSide) |
1639 | 5.97k | { |
1640 | 5.97k | auto toStore = _rightHandSide; |
1641 | 5.97k | auto const* lastExpr = &_expr; |
1642 | 15.5k | while (true) |
1643 | 15.5k | { |
1644 | 15.5k | if (auto const* indexAccess = dynamic_cast<IndexAccess const*>(lastExpr)) |
1645 | 7.99k | { |
1646 | 7.99k | auto const& base = indexAccess->baseExpression(); |
1647 | 7.99k | if (dynamic_cast<Identifier const*>(&base)) |
1648 | 4.89k | base.accept(*this); |
1649 | | |
1650 | 7.99k | Type const* baseType = base.annotation().type; |
1651 | 7.99k | auto indexExpr = expr(*indexAccess->indexExpression(), keyType(baseType)); |
1652 | 7.99k | auto symbArray = std::dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(base)); |
1653 | 7.99k | solAssert(symbArray, ""); |
1654 | 7.99k | toStore = smtutil::Expression::tuple_constructor( |
1655 | 7.99k | smtutil::Expression(std::make_shared<smtutil::SortSort>(smt::smtSort(*baseType)), baseType->toString(true)), |
1656 | 7.99k | {smtutil::Expression::store(symbArray->elements(), indexExpr, toStore), symbArray->length()} |
1657 | 7.99k | ); |
1658 | 7.99k | defineExpr(*indexAccess, smtutil::Expression::select( |
1659 | 7.99k | symbArray->elements(), |
1660 | 7.99k | indexExpr |
1661 | 7.99k | )); |
1662 | 7.99k | lastExpr = &indexAccess->baseExpression(); |
1663 | 7.99k | } |
1664 | 7.59k | else if (auto const* memberAccess = dynamic_cast<MemberAccess const*>(lastExpr)) |
1665 | 1.62k | { |
1666 | 1.62k | auto const& base = memberAccess->expression(); |
1667 | 1.62k | if (dynamic_cast<Identifier const*>(&base)) |
1668 | 1.07k | base.accept(*this); |
1669 | | |
1670 | 1.62k | if ( |
1671 | 1.62k | auto const* structType = dynamic_cast<StructType const*>(base.annotation().type); |
1672 | 1.62k | structType && structType->recursive() |
1673 | 1.62k | ) |
1674 | 4 | { |
1675 | 4 | m_unsupportedErrors.warning( |
1676 | 4 | 4375_error, |
1677 | 4 | memberAccess->location(), |
1678 | 4 | "Assertion checker does not support recursive structs." |
1679 | 4 | ); |
1680 | 4 | return; |
1681 | 4 | } |
1682 | 1.62k | if (auto varDecl = identifierToVariable(*memberAccess)) |
1683 | 4 | { |
1684 | 4 | if (varDecl->hasReferenceOrMappingType()) |
1685 | 4 | resetReferences(*varDecl); |
1686 | | |
1687 | 4 | assignment(*varDecl, toStore); |
1688 | 4 | break; |
1689 | 4 | } |
1690 | | |
1691 | 1.62k | auto symbStruct = std::dynamic_pointer_cast<smt::SymbolicStructVariable>(m_context.expression(base)); |
1692 | 1.62k | solAssert(symbStruct, ""); |
1693 | 1.62k | symbStruct->assignMember(memberAccess->memberName(), toStore); |
1694 | 1.62k | toStore = symbStruct->currentValue(); |
1695 | 1.62k | defineExpr(*memberAccess, symbStruct->member(memberAccess->memberName())); |
1696 | 1.62k | lastExpr = &memberAccess->expression(); |
1697 | 1.62k | } |
1698 | 5.97k | else if (auto const& id = dynamic_cast<Identifier const*>(lastExpr)) |
1699 | 5.96k | { |
1700 | 5.96k | auto varDecl = identifierToVariable(*id); |
1701 | 5.96k | solAssert(varDecl, ""); |
1702 | | |
1703 | 5.96k | if (varDecl->hasReferenceOrMappingType()) |
1704 | 5.96k | resetReferences(*varDecl); |
1705 | | |
1706 | 5.96k | assignment(*varDecl, toStore); |
1707 | 5.96k | defineExpr(*id, currentValue(*varDecl)); |
1708 | 5.96k | break; |
1709 | 5.96k | } |
1710 | 10 | else |
1711 | 10 | { |
1712 | 10 | auto type = lastExpr->annotation().type; |
1713 | 10 | if ( |
1714 | 10 | dynamic_cast<ReferenceType const*>(type) || |
1715 | 4 | dynamic_cast<MappingType const*>(type) |
1716 | 10 | ) |
1717 | 10 | resetReferences(type); |
1718 | | |
1719 | 10 | assignment(*m_context.expression(*lastExpr), toStore); |
1720 | 10 | break; |
1721 | 10 | } |
1722 | 15.5k | } |
1723 | 5.97k | } |
1724 | | |
1725 | | void SMTEncoder::arrayPush(FunctionCall const& _funCall) |
1726 | 2.22k | { |
1727 | 2.22k | auto memberAccess = dynamic_cast<MemberAccess const*>(&_funCall.expression()); |
1728 | 2.22k | solAssert(memberAccess, ""); |
1729 | 2.22k | auto symbArray = std::dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(memberAccess->expression())); |
1730 | 2.22k | solAssert(symbArray, ""); |
1731 | 2.22k | auto oldLength = symbArray->length(); |
1732 | 2.22k | m_context.addAssertion(oldLength >= 0); |
1733 | | // Real world assumption: the array length is assumed to not overflow. |
1734 | | // This assertion guarantees that both the current and updated lengths have the above property. |
1735 | 2.22k | m_context.addAssertion(oldLength + 1 < (smt::maxValue(*TypeProvider::uint256()) - 1)); |
1736 | | |
1737 | 2.22k | auto const& arguments = _funCall.arguments(); |
1738 | 2.22k | auto arrayType = dynamic_cast<ArrayType const*>(symbArray->type()); |
1739 | 2.22k | solAssert(arrayType, ""); |
1740 | 2.22k | auto elementType = arrayType->baseType(); |
1741 | 2.22k | smtutil::Expression element = arguments.empty() ? |
1742 | 944 | smt::zeroValue(elementType) : |
1743 | 2.22k | expr(*arguments.front(), elementType); |
1744 | 2.22k | smtutil::Expression store = smtutil::Expression::store( |
1745 | 2.22k | symbArray->elements(), |
1746 | 2.22k | oldLength, |
1747 | 2.22k | element |
1748 | 2.22k | ); |
1749 | 2.22k | symbArray->increaseIndex(); |
1750 | 2.22k | m_context.addAssertion(symbArray->elements() == store); |
1751 | 2.22k | m_context.addAssertion(symbArray->length() == oldLength + 1); |
1752 | | |
1753 | 2.22k | if (arguments.empty()) |
1754 | 944 | defineExpr(_funCall, element); |
1755 | | |
1756 | 2.22k | assignment(memberAccess->expression(), symbArray->currentValue()); |
1757 | 2.22k | } |
1758 | | |
1759 | | void SMTEncoder::arrayPop(FunctionCall const& _funCall) |
1760 | 354 | { |
1761 | 354 | auto memberAccess = dynamic_cast<MemberAccess const*>(cleanExpression(_funCall.expression())); |
1762 | 354 | solAssert(memberAccess, ""); |
1763 | 354 | auto symbArray = std::dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(memberAccess->expression())); |
1764 | 354 | solAssert(symbArray, ""); |
1765 | | |
1766 | 354 | makeArrayPopVerificationTarget(_funCall); |
1767 | | |
1768 | 354 | auto oldElements = symbArray->elements(); |
1769 | 354 | auto oldLength = symbArray->length(); |
1770 | | |
1771 | 354 | symbArray->increaseIndex(); |
1772 | 354 | m_context.addAssertion(symbArray->elements() == oldElements); |
1773 | 354 | auto newLength = smtutil::Expression::ite( |
1774 | 354 | oldLength > 0, |
1775 | 354 | oldLength - 1, |
1776 | 354 | 0 |
1777 | 354 | ); |
1778 | 354 | m_context.addAssertion(symbArray->length() == newLength); |
1779 | | |
1780 | 354 | assignment(memberAccess->expression(), symbArray->currentValue()); |
1781 | 354 | } |
1782 | | |
1783 | | void SMTEncoder::defineGlobalVariable(std::string const& _name, Expression const& _expr, bool _increaseIndex) |
1784 | 2.75k | { |
1785 | 2.75k | if (!m_context.knownGlobalSymbol(_name)) |
1786 | 1.62k | { |
1787 | 1.62k | bool abstract = m_context.createGlobalSymbol(_name, _expr); |
1788 | 1.62k | if (abstract) |
1789 | 46 | m_unsupportedErrors.warning( |
1790 | 46 | 1695_error, |
1791 | 46 | _expr.location(), |
1792 | 46 | "Assertion checker does not yet support this global variable." |
1793 | 46 | ); |
1794 | 1.62k | } |
1795 | 1.13k | else if (_increaseIndex) |
1796 | 44 | m_context.globalSymbol(_name)->increaseIndex(); |
1797 | | // The default behavior is not to increase the index since |
1798 | | // most of the global values stay the same throughout a tx. |
1799 | 2.75k | if (isSupportedType(*_expr.annotation().type)) |
1800 | 66 | defineExpr(_expr, m_context.globalSymbol(_name)->currentValue()); |
1801 | 2.75k | } |
1802 | | |
1803 | | bool SMTEncoder::shortcutRationalNumber(Expression const& _expr) |
1804 | 40.0k | { |
1805 | 40.0k | RationalNumberType const* rationalType = isConstant(_expr); |
1806 | 40.0k | if (!rationalType) |
1807 | 34.8k | return false; |
1808 | | |
1809 | 5.16k | if (rationalType->isNegative()) |
1810 | 2.60k | defineExpr(_expr, smtutil::Expression(u2s(rationalType->literalValue(nullptr)))); |
1811 | 2.56k | else |
1812 | 2.56k | defineExpr(_expr, smtutil::Expression(rationalType->literalValue(nullptr))); |
1813 | 5.16k | return true; |
1814 | 40.0k | } |
1815 | | |
1816 | | void SMTEncoder::arithmeticOperation(BinaryOperation const& _op) |
1817 | 18.2k | { |
1818 | 18.2k | auto type = _op.annotation().commonType; |
1819 | 18.2k | solAssert(type, ""); |
1820 | 18.2k | solAssert(type->category() == Type::Category::Integer || type->category() == Type::Category::FixedPoint, ""); |
1821 | 18.2k | switch (_op.getOperator()) |
1822 | 18.2k | { |
1823 | 2.52k | case Token::Add: |
1824 | 8.66k | case Token::Sub: |
1825 | 12.7k | case Token::Mul: |
1826 | 13.5k | case Token::Div: |
1827 | 14.2k | case Token::Mod: |
1828 | 14.2k | { |
1829 | 14.2k | auto values = arithmeticOperation( |
1830 | 14.2k | _op.getOperator(), |
1831 | 14.2k | expr(_op.leftExpression()), |
1832 | 14.2k | expr(_op.rightExpression()), |
1833 | 14.2k | _op.annotation().commonType, |
1834 | 14.2k | _op |
1835 | 14.2k | ); |
1836 | 14.2k | defineExpr(_op, values.first); |
1837 | 14.2k | break; |
1838 | 13.5k | } |
1839 | 3.99k | default: |
1840 | 3.99k | m_unsupportedErrors.warning( |
1841 | 3.99k | 5188_error, |
1842 | 3.99k | _op.location(), |
1843 | 3.99k | "Assertion checker does not yet implement this operator." |
1844 | 3.99k | ); |
1845 | 18.2k | } |
1846 | 18.2k | } |
1847 | | |
1848 | | std::pair<smtutil::Expression, smtutil::Expression> SMTEncoder::arithmeticOperation( |
1849 | | Token _op, |
1850 | | smtutil::Expression const& _left, |
1851 | | smtutil::Expression const& _right, |
1852 | | Type const* _commonType, |
1853 | | Expression const& _operation |
1854 | | ) |
1855 | 17.3k | { |
1856 | 17.3k | static std::set<Token> validOperators{ |
1857 | 17.3k | Token::Add, |
1858 | 17.3k | Token::Sub, |
1859 | 17.3k | Token::Mul, |
1860 | 17.3k | Token::Div, |
1861 | 17.3k | Token::Mod |
1862 | 17.3k | }; |
1863 | 17.3k | solAssert(validOperators.count(_op), ""); |
1864 | 17.3k | solAssert(_commonType, ""); |
1865 | 17.3k | solAssert( |
1866 | 17.3k | _commonType->category() == Type::Category::Integer || _commonType->category() == Type::Category::FixedPoint, |
1867 | 17.3k | "" |
1868 | 17.3k | ); |
1869 | | |
1870 | 17.3k | IntegerType const* intType = nullptr; |
1871 | 17.3k | if (auto type = dynamic_cast<IntegerType const*>(_commonType)) |
1872 | 17.0k | intType = type; |
1873 | 340 | else |
1874 | 340 | intType = TypeProvider::uint256(); |
1875 | | |
1876 | 17.3k | auto valueUnbounded = [&]() -> smtutil::Expression { |
1877 | 17.3k | switch (_op) |
1878 | 17.3k | { |
1879 | 5.02k | case Token::Add: return _left + _right; |
1880 | 6.64k | case Token::Sub: return _left - _right; |
1881 | 4.21k | case Token::Mul: return _left * _right; |
1882 | 822 | case Token::Div: return divModWithSlacks(_left, _right, *intType).first; |
1883 | 697 | case Token::Mod: return divModWithSlacks(_left, _right, *intType).second; |
1884 | 0 | default: solAssert(false, ""); |
1885 | 17.3k | } |
1886 | 17.3k | }(); |
1887 | | |
1888 | 17.3k | if (m_checked) |
1889 | 17.1k | return {valueUnbounded, valueUnbounded}; |
1890 | | |
1891 | 249 | if (_op == Token::Div || _op == Token::Mod) |
1892 | 20 | { |
1893 | | // mod and unsigned division never underflow/overflow |
1894 | 20 | if (_op == Token::Mod || !intType->isSigned()) |
1895 | 20 | return {valueUnbounded, valueUnbounded}; |
1896 | | |
1897 | | // The only case where division overflows is |
1898 | | // - type is signed |
1899 | | // - LHS is type.min |
1900 | | // - RHS is -1 |
1901 | | // the result is then -(type.min), which wraps back to type.min |
1902 | 0 | smtutil::Expression maxLeft = _left == smt::minValue(*intType); |
1903 | 0 | smtutil::Expression minusOneRight = _right == std::numeric_limits<size_t >::max(); |
1904 | 0 | smtutil::Expression wrap = smtutil::Expression::ite(maxLeft && minusOneRight, smt::minValue(*intType), valueUnbounded); |
1905 | 0 | return {wrap, valueUnbounded}; |
1906 | 20 | } |
1907 | | |
1908 | 229 | auto symbMin = smt::minValue(*intType); |
1909 | 229 | auto symbMax = smt::maxValue(*intType); |
1910 | | |
1911 | 229 | smtutil::Expression intValueRange = (0 - symbMin) + symbMax + 1; |
1912 | 229 | std::string suffix = std::to_string(_operation.id()) + "_" + std::to_string(m_context.newUniqueId()); |
1913 | 229 | smt::SymbolicIntVariable k(intType, intType, "k_" + suffix, m_context); |
1914 | 229 | smt::SymbolicIntVariable m(intType, intType, "m_" + suffix, m_context); |
1915 | | |
1916 | | // To wrap around valueUnbounded in case of overflow or underflow, we replace it with a k, given: |
1917 | | // 1. k + m * intValueRange = valueUnbounded |
1918 | | // 2. k is in range of the desired integer type |
1919 | 229 | auto wrap = k.currentValue(); |
1920 | 229 | m_context.addAssertion(valueUnbounded == (k.currentValue() + intValueRange * m.currentValue())); |
1921 | 229 | m_context.addAssertion(k.currentValue() >= symbMin); |
1922 | 229 | m_context.addAssertion(k.currentValue() <= symbMax); |
1923 | | |
1924 | | // TODO this could be refined: |
1925 | | // for unsigned types it's enough to check only the upper bound. |
1926 | 229 | auto value = smtutil::Expression::ite( |
1927 | 229 | valueUnbounded > symbMax, |
1928 | 229 | wrap, |
1929 | 229 | smtutil::Expression::ite( |
1930 | 229 | valueUnbounded < symbMin, |
1931 | 229 | wrap, |
1932 | 229 | valueUnbounded |
1933 | 229 | ) |
1934 | 229 | ); |
1935 | | |
1936 | 229 | return {value, valueUnbounded}; |
1937 | 249 | } |
1938 | | |
1939 | | smtutil::Expression SMTEncoder::bitwiseOperation( |
1940 | | Token _op, |
1941 | | smtutil::Expression const& _left, |
1942 | | smtutil::Expression const& _right, |
1943 | | Type const* _commonType |
1944 | | ) |
1945 | 1.29k | { |
1946 | 1.29k | static std::set<Token> validOperators{ |
1947 | 1.29k | Token::BitAnd, |
1948 | 1.29k | Token::BitOr, |
1949 | 1.29k | Token::BitXor, |
1950 | 1.29k | Token::SHL, |
1951 | 1.29k | Token::SHR, |
1952 | 1.29k | Token::SAR |
1953 | 1.29k | }; |
1954 | 1.29k | solAssert(validOperators.count(_op), ""); |
1955 | 1.29k | solAssert(_commonType, ""); |
1956 | | |
1957 | 1.29k | auto [bvSize, isSigned] = smt::typeBvSizeAndSignedness(_commonType); |
1958 | | |
1959 | 1.29k | auto bvLeft = smtutil::Expression::int2bv(_left, bvSize); |
1960 | 1.29k | auto bvRight = smtutil::Expression::int2bv(_right, bvSize); |
1961 | | |
1962 | 1.29k | std::optional<smtutil::Expression> result; |
1963 | 1.29k | switch (_op) |
1964 | 1.29k | { |
1965 | 498 | case Token::BitAnd: |
1966 | 498 | result = bvLeft & bvRight; |
1967 | 498 | break; |
1968 | 215 | case Token::BitOr: |
1969 | 215 | result = bvLeft | bvRight; |
1970 | 215 | break; |
1971 | 302 | case Token::BitXor: |
1972 | 302 | result = bvLeft ^ bvRight; |
1973 | 302 | break; |
1974 | 110 | case Token::SHL: |
1975 | 110 | result = bvLeft << bvRight; |
1976 | 110 | break; |
1977 | 4 | case Token::SHR: |
1978 | 4 | result = bvLeft >> bvRight; |
1979 | 4 | break; |
1980 | 161 | case Token::SAR: |
1981 | 161 | result = isSigned ? |
1982 | 12 | smtutil::Expression::ashr(bvLeft, bvRight) : |
1983 | 161 | bvLeft >> bvRight; |
1984 | 161 | break; |
1985 | 0 | default: |
1986 | 0 | solAssert(false, ""); |
1987 | 1.29k | } |
1988 | | |
1989 | 1.29k | solAssert(result.has_value(), ""); |
1990 | 1.29k | return smtutil::Expression::bv2int(*result, isSigned); |
1991 | 1.29k | } |
1992 | | |
1993 | | void SMTEncoder::compareOperation(BinaryOperation const& _op) |
1994 | 9.44k | { |
1995 | 9.44k | auto commonType = _op.annotation().commonType; |
1996 | 9.44k | solAssert(commonType, ""); |
1997 | | |
1998 | 9.44k | if (isSupportedType(*commonType)) |
1999 | 9.38k | { |
2000 | 9.38k | smtutil::Expression left(expr(_op.leftExpression(), commonType)); |
2001 | 9.38k | smtutil::Expression right(expr(_op.rightExpression(), commonType)); |
2002 | 9.38k | Token op = _op.getOperator(); |
2003 | 9.38k | std::shared_ptr<smtutil::Expression> value; |
2004 | 9.38k | if (smt::isNumber(*commonType)) |
2005 | 9.21k | { |
2006 | 9.21k | value = std::make_shared<smtutil::Expression>( |
2007 | 9.21k | op == Token::Equal ? (left == right) : |
2008 | 9.21k | op == Token::NotEqual ? (left != right) : |
2009 | 4.58k | op == Token::LessThan ? (left < right) : |
2010 | 3.92k | op == Token::LessThanOrEqual ? (left <= right) : |
2011 | 1.90k | op == Token::GreaterThan ? (left > right) : |
2012 | 1.72k | /*op == Token::GreaterThanOrEqual*/ (left >= right) |
2013 | 9.21k | ); |
2014 | 9.21k | } |
2015 | 167 | else // Bool |
2016 | 167 | { |
2017 | 167 | solUnimplementedAssert(smt::isBool(*commonType), "Operation not yet supported"); |
2018 | 167 | value = std::make_shared<smtutil::Expression>( |
2019 | 167 | op == Token::Equal ? (left == right) : |
2020 | 167 | /*op == Token::NotEqual*/ (left != right) |
2021 | 167 | ); |
2022 | 167 | } |
2023 | | // TODO: check that other values for op are not possible. |
2024 | 9.38k | defineExpr(_op, *value); |
2025 | 9.38k | } |
2026 | 66 | else |
2027 | 66 | m_unsupportedErrors.warning( |
2028 | 66 | 7229_error, |
2029 | 66 | _op.location(), |
2030 | 66 | "Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for comparisons" |
2031 | 66 | ); |
2032 | 9.44k | } |
2033 | | |
2034 | | void SMTEncoder::booleanOperation(BinaryOperation const& _op) |
2035 | 519 | { |
2036 | 519 | solAssert(_op.getOperator() == Token::And || _op.getOperator() == Token::Or, ""); |
2037 | 519 | solAssert(_op.annotation().commonType, ""); |
2038 | 519 | solAssert(_op.annotation().commonType->category() == Type::Category::Bool, ""); |
2039 | | // @TODO check that both of them are not constant |
2040 | 519 | _op.leftExpression().accept(*this); |
2041 | 519 | if (_op.getOperator() == Token::And) |
2042 | 303 | { |
2043 | 303 | auto indicesAfterSecond = visitBranch(&_op.rightExpression(), expr(_op.leftExpression())).first; |
2044 | 303 | mergeVariables(!expr(_op.leftExpression()), copyVariableIndices(), indicesAfterSecond); |
2045 | 303 | defineExpr(_op, expr(_op.leftExpression()) && expr(_op.rightExpression())); |
2046 | 303 | } |
2047 | 216 | else |
2048 | 216 | { |
2049 | 216 | auto indicesAfterSecond = visitBranch(&_op.rightExpression(), !expr(_op.leftExpression())).first; |
2050 | 216 | mergeVariables(expr(_op.leftExpression()), copyVariableIndices(), indicesAfterSecond); |
2051 | 216 | defineExpr(_op, expr(_op.leftExpression()) || expr(_op.rightExpression())); |
2052 | 216 | } |
2053 | 519 | } |
2054 | | |
2055 | | void SMTEncoder::bitwiseOperation(BinaryOperation const& _op) |
2056 | 830 | { |
2057 | 830 | auto op = _op.getOperator(); |
2058 | 830 | solAssert(TokenTraits::isBitOp(op) || TokenTraits::isShiftOp(op), ""); |
2059 | 830 | auto commonType = _op.annotation().commonType; |
2060 | 830 | solAssert(commonType, ""); |
2061 | | |
2062 | 830 | defineExpr(_op, bitwiseOperation( |
2063 | 830 | _op.getOperator(), |
2064 | 830 | expr(_op.leftExpression(), commonType), |
2065 | 830 | expr(_op.rightExpression(), commonType), |
2066 | 830 | commonType |
2067 | 830 | )); |
2068 | 830 | } |
2069 | | |
2070 | | void SMTEncoder::bitwiseNotOperation(UnaryOperation const& _op) |
2071 | 2.15k | { |
2072 | 2.15k | solAssert(_op.getOperator() == Token::BitNot, ""); |
2073 | | |
2074 | 2.15k | auto [bvSize, isSigned] = smt::typeBvSizeAndSignedness(_op.annotation().type); |
2075 | | |
2076 | 2.15k | auto bvOperand = smtutil::Expression::int2bv(expr(_op.subExpression(), _op.annotation().type), bvSize); |
2077 | 2.15k | defineExpr(_op, smtutil::Expression::bv2int(~bvOperand, isSigned)); |
2078 | 2.15k | } |
2079 | | |
2080 | | std::pair<smtutil::Expression, smtutil::Expression> SMTEncoder::divModWithSlacks( |
2081 | | smtutil::Expression _left, |
2082 | | smtutil::Expression _right, |
2083 | | IntegerType const& _type |
2084 | | ) |
2085 | 1.62k | { |
2086 | 1.62k | if (m_settings.divModNoSlacks) |
2087 | 1.62k | return {_left / _right, _left % _right}; |
2088 | | |
2089 | 0 | IntegerType const* intType = &_type; |
2090 | 0 | std::string suffix = "div_mod_" + std::to_string(m_context.newUniqueId()); |
2091 | 0 | smt::SymbolicIntVariable dSymb(intType, intType, "d_" + suffix, m_context); |
2092 | 0 | smt::SymbolicIntVariable rSymb(intType, intType, "r_" + suffix, m_context); |
2093 | 0 | auto d = dSymb.currentValue(); |
2094 | 0 | auto r = rSymb.currentValue(); |
2095 | | |
2096 | | // x / y = d and x % y = r iff d * y + r = x and |
2097 | | // either x >= 0 and 0 <= r < abs(y) (or just 0 <= r < y for unsigned) |
2098 | | // or x < 0 and -abs(y) < r <= 0 |
2099 | 0 | m_context.addAssertion(((d * _right) + r) == _left); |
2100 | 0 | if (_type.isSigned()) |
2101 | 0 | m_context.addAssertion( |
2102 | 0 | (_left >= 0 && 0 <= r && (_right == 0 || r < smtutil::abs(_right))) || |
2103 | 0 | (_left < 0 && ((_right == 0 || 0 - smtutil::abs(_right) < r) && r <= 0)) |
2104 | 0 | ); |
2105 | 0 | else // unsigned version |
2106 | 0 | m_context.addAssertion(0 <= r && (_right == 0 || r < _right)); |
2107 | |
|
2108 | 0 | auto divResult = smtutil::Expression::ite(_right == 0, 0, d); |
2109 | 0 | auto modResult = smtutil::Expression::ite(_right == 0, 0, r); |
2110 | 0 | return {divResult, modResult}; |
2111 | 1.62k | } |
2112 | | |
2113 | | void SMTEncoder::assignment(Expression const& _left, smtutil::Expression const& _right) |
2114 | 5.24k | { |
2115 | 5.24k | assignment(_left, _right, _left.annotation().type); |
2116 | 5.24k | } |
2117 | | |
2118 | | void SMTEncoder::assignment( |
2119 | | Expression const& _left, |
2120 | | smtutil::Expression const& _right, |
2121 | | Type const* _type |
2122 | | ) |
2123 | 25.0k | { |
2124 | 25.0k | solAssert( |
2125 | 25.0k | _left.annotation().type->category() != Type::Category::Tuple, |
2126 | 25.0k | "Tuple assignments should be handled by tupleAssignment." |
2127 | 25.0k | ); |
2128 | | |
2129 | 25.0k | Expression const* left = cleanExpression(_left); |
2130 | | |
2131 | 25.0k | if (!isSupportedType(*_type)) |
2132 | 30 | { |
2133 | | // Give it a new index anyway to keep the SSA scheme sound. |
2134 | 30 | if (auto varDecl = identifierToVariable(*left)) |
2135 | 30 | m_context.newValue(*varDecl); |
2136 | 30 | } |
2137 | 25.0k | else if (auto varDecl = identifierToVariable(*left)) |
2138 | 18.9k | { |
2139 | 18.9k | if (varDecl->hasReferenceOrMappingType()) |
2140 | 9.67k | resetReferences(*varDecl); |
2141 | 18.9k | assignment(*varDecl, _right); |
2142 | 18.9k | } |
2143 | 6.07k | else if ( |
2144 | 6.07k | dynamic_cast<IndexAccess const*>(left) || |
2145 | 1.44k | dynamic_cast<MemberAccess const*>(left) |
2146 | 6.07k | ) |
2147 | 5.89k | indexOrMemberAssignment(*left, _right); |
2148 | 178 | else if (auto const* funCall = dynamic_cast<FunctionCall const*>(left)) |
2149 | 178 | { |
2150 | 178 | if (auto funType = dynamic_cast<FunctionType const*>(funCall->expression().annotation().type)) |
2151 | 178 | { |
2152 | 178 | if (funType->kind() == FunctionType::Kind::ArrayPush) |
2153 | 174 | { |
2154 | 174 | auto memberAccess = dynamic_cast<MemberAccess const*>(&funCall->expression()); |
2155 | 174 | solAssert(memberAccess, ""); |
2156 | 174 | auto symbArray = std::dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(memberAccess->expression())); |
2157 | 174 | solAssert(symbArray, ""); |
2158 | | |
2159 | 174 | auto oldLength = symbArray->length(); |
2160 | 174 | auto store = smtutil::Expression::store( |
2161 | 174 | symbArray->elements(), |
2162 | 174 | symbArray->length() - 1, |
2163 | 174 | _right |
2164 | 174 | ); |
2165 | 174 | symbArray->increaseIndex(); |
2166 | 174 | m_context.addAssertion(symbArray->elements() == store); |
2167 | 174 | m_context.addAssertion(symbArray->length() == oldLength); |
2168 | 174 | assignment(memberAccess->expression(), symbArray->currentValue()); |
2169 | 174 | } |
2170 | 4 | else if (funType->kind() == FunctionType::Kind::Internal) |
2171 | 4 | { |
2172 | 4 | for (auto type: replaceUserTypes(funType->returnParameterTypes())) |
2173 | 4 | if (type->category() == Type::Category::Mapping || dynamic_cast<ReferenceType const*>(type)) |
2174 | 4 | resetReferences(type); |
2175 | 4 | } |
2176 | 178 | } |
2177 | 178 | } |
2178 | 0 | else |
2179 | 178 | solAssert(false, ""); |
2180 | 25.0k | } |
2181 | | |
2182 | | void SMTEncoder::tupleAssignment(Expression const& _left, Expression const& _right) |
2183 | 367 | { |
2184 | 367 | auto lTuple = dynamic_cast<TupleExpression const*>(&innermostTuple(_left)); |
2185 | 367 | solAssert(lTuple, ""); |
2186 | 367 | Expression const& right = innermostTuple(_right); |
2187 | | |
2188 | 367 | auto const& lComponents = lTuple->components(); |
2189 | | |
2190 | | // If both sides are tuple expressions, we individually and potentially |
2191 | | // recursively assign each pair of components. |
2192 | | // This is because of potential type conversion. |
2193 | 367 | if (auto rTuple = dynamic_cast<TupleExpression const*>(&right)) |
2194 | 295 | { |
2195 | 295 | auto const& rComponents = rTuple->components(); |
2196 | 295 | solAssert(lComponents.size() == rComponents.size(), ""); |
2197 | 918 | for (unsigned i = 0; i < lComponents.size(); ++i) |
2198 | 623 | { |
2199 | 623 | if (!lComponents.at(i) || !rComponents.at(i)) |
2200 | 82 | continue; |
2201 | 541 | auto const& lExpr = *lComponents.at(i); |
2202 | 541 | auto const& rExpr = *rComponents.at(i); |
2203 | 541 | if (lExpr.annotation().type->category() == Type::Category::Tuple) |
2204 | 30 | tupleAssignment(lExpr, rExpr); |
2205 | 511 | else |
2206 | 511 | { |
2207 | 511 | auto type = lExpr.annotation().type; |
2208 | 511 | assignment(lExpr, expr(rExpr, type), type); |
2209 | 511 | } |
2210 | 541 | } |
2211 | 295 | } |
2212 | 72 | else |
2213 | 72 | { |
2214 | 72 | auto rType = dynamic_cast<TupleType const*>(right.annotation().type); |
2215 | 72 | solAssert(rType, ""); |
2216 | | |
2217 | 72 | auto const& rComponents = rType->components(); |
2218 | 72 | solAssert(lComponents.size() == rComponents.size(), ""); |
2219 | | |
2220 | 72 | auto symbRight = expr(right); |
2221 | 72 | solAssert(symbRight.sort->kind == smtutil::Kind::Tuple, ""); |
2222 | | |
2223 | 281 | for (unsigned i = 0; i < lComponents.size(); ++i) |
2224 | 209 | if (auto component = lComponents.at(i); component && rComponents.at(i)) |
2225 | 143 | assignment(*component, smtutil::Expression::tuple_get(symbRight, i), component->annotation().type); |
2226 | 72 | } |
2227 | 367 | } |
2228 | | |
2229 | | smtutil::Expression SMTEncoder::compoundAssignment(Assignment const& _assignment) |
2230 | 1.13k | { |
2231 | 1.13k | static std::map<Token, Token> const compoundToArithmetic{ |
2232 | 1.13k | {Token::AssignAdd, Token::Add}, |
2233 | 1.13k | {Token::AssignSub, Token::Sub}, |
2234 | 1.13k | {Token::AssignMul, Token::Mul}, |
2235 | 1.13k | {Token::AssignDiv, Token::Div}, |
2236 | 1.13k | {Token::AssignMod, Token::Mod} |
2237 | 1.13k | }; |
2238 | 1.13k | static std::map<Token, Token> const compoundToBitwise{ |
2239 | 1.13k | {Token::AssignBitAnd, Token::BitAnd}, |
2240 | 1.13k | {Token::AssignBitOr, Token::BitOr}, |
2241 | 1.13k | {Token::AssignBitXor, Token::BitXor}, |
2242 | 1.13k | {Token::AssignShl, Token::SHL}, |
2243 | 1.13k | {Token::AssignShr, Token::SHR}, |
2244 | 1.13k | {Token::AssignSar, Token::SAR} |
2245 | 1.13k | }; |
2246 | 1.13k | Token op = _assignment.assignmentOperator(); |
2247 | 1.13k | solAssert(compoundToArithmetic.count(op) || compoundToBitwise.count(op), ""); |
2248 | | |
2249 | 1.13k | auto decl = identifierToVariable(_assignment.leftHandSide()); |
2250 | | |
2251 | 1.13k | if (compoundToBitwise.count(op)) |
2252 | 460 | return bitwiseOperation( |
2253 | 460 | compoundToBitwise.at(op), |
2254 | 460 | decl ? currentValue(*decl) : expr(_assignment.leftHandSide(), _assignment.annotation().type), |
2255 | 460 | expr(_assignment.rightHandSide(), _assignment.annotation().type), |
2256 | 460 | _assignment.annotation().type |
2257 | 460 | ); |
2258 | | |
2259 | 674 | auto values = arithmeticOperation( |
2260 | 674 | compoundToArithmetic.at(op), |
2261 | 674 | decl ? currentValue(*decl) : expr(_assignment.leftHandSide(), _assignment.annotation().type), |
2262 | 674 | expr(_assignment.rightHandSide(), _assignment.annotation().type), |
2263 | 674 | _assignment.annotation().type, |
2264 | 674 | _assignment |
2265 | 674 | ); |
2266 | 674 | return values.first; |
2267 | 1.13k | } |
2268 | | |
2269 | | void SMTEncoder::expressionToTupleAssignment(std::vector<std::shared_ptr<VariableDeclaration>> const& _variables, Expression const& _rhs) |
2270 | 5.58k | { |
2271 | 5.58k | auto symbolicVar = m_context.expression(_rhs); |
2272 | 5.58k | if (_variables.size() > 1) |
2273 | 329 | { |
2274 | 329 | auto symbTuple = std::dynamic_pointer_cast<smt::SymbolicTupleVariable>(symbolicVar); |
2275 | 329 | solAssert(symbTuple, ""); |
2276 | 329 | auto const& symbComponents = symbTuple->components(); |
2277 | 329 | solAssert(symbComponents.size() == _variables.size(), ""); |
2278 | 329 | auto tupleType = dynamic_cast<TupleType const*>(_rhs.annotation().type); |
2279 | 329 | solAssert(tupleType, ""); |
2280 | 329 | auto const& typeComponents = tupleType->components(); |
2281 | 329 | solAssert(typeComponents.size() == symbComponents.size(), ""); |
2282 | 1.10k | for (unsigned i = 0; i < symbComponents.size(); ++i) |
2283 | 778 | { |
2284 | 778 | auto param = _variables.at(i); |
2285 | 778 | if (param) |
2286 | 606 | { |
2287 | 606 | solAssert(m_context.knownVariable(*param), ""); |
2288 | 606 | assignment(*param, symbTuple->component(i, typeComponents[i], param->type())); |
2289 | 606 | } |
2290 | 778 | } |
2291 | 329 | } |
2292 | 5.25k | else if (_variables.size() == 1) |
2293 | 5.25k | { |
2294 | 5.25k | auto const& var = *_variables.front(); |
2295 | 5.25k | solAssert(m_context.knownVariable(var), ""); |
2296 | 5.25k | assignment(var, _rhs); |
2297 | 5.25k | } |
2298 | 5.58k | } |
2299 | | |
2300 | | void SMTEncoder::assignment(VariableDeclaration const& _variable, Expression const& _value) |
2301 | 9.95k | { |
2302 | | // In general, at this point, the SMT sorts of _variable and _value are the same, |
2303 | | // even if there is implicit conversion. |
2304 | | // This is a special case where the SMT sorts are different. |
2305 | | // For now we are unaware of other cases where this happens, but if they do appear |
2306 | | // we should extract this into an `implicitConversion` function. |
2307 | 9.95k | assignment(_variable, expr(_value, _variable.type())); |
2308 | 9.95k | } |
2309 | | |
2310 | | void SMTEncoder::assignment(VariableDeclaration const& _variable, smtutil::Expression const& _value) |
2311 | 47.8k | { |
2312 | 47.8k | Type const* type = _variable.type(); |
2313 | 47.8k | if (type->category() == Type::Category::Mapping) |
2314 | 1.39k | arrayAssignment(); |
2315 | 47.8k | assignment(*m_context.variable(_variable), _value); |
2316 | 47.8k | } |
2317 | | |
2318 | | void SMTEncoder::assignment(smt::SymbolicVariable& _symVar, smtutil::Expression const& _value) |
2319 | 23.3k | { |
2320 | 23.3k | m_context.addAssertion(_symVar.increaseIndex() == _value); |
2321 | 23.3k | } |
2322 | | |
2323 | | std::pair<SMTEncoder::VariableIndices, smtutil::Expression> SMTEncoder::visitBranch( |
2324 | | ASTNode const* _statement, |
2325 | | smtutil::Expression _condition |
2326 | | ) |
2327 | 3.77k | { |
2328 | 3.77k | return visitBranch(_statement, &_condition); |
2329 | 3.77k | } |
2330 | | |
2331 | | std::pair<SMTEncoder::VariableIndices, smtutil::Expression> SMTEncoder::visitBranch( |
2332 | | ASTNode const* _statement, |
2333 | | smtutil::Expression const* _condition |
2334 | | ) |
2335 | 4.07k | { |
2336 | 4.07k | auto indicesBeforeBranch = copyVariableIndices(); |
2337 | 4.07k | if (_condition) |
2338 | 3.77k | pushPathCondition(*_condition); |
2339 | 4.07k | _statement->accept(*this); |
2340 | 4.07k | auto pathConditionOnExit = currentPathConditions(); |
2341 | 4.07k | if (_condition) |
2342 | 3.77k | popPathCondition(); |
2343 | 4.07k | auto indicesAfterBranch = copyVariableIndices(); |
2344 | 4.07k | resetVariableIndices(indicesBeforeBranch); |
2345 | 4.07k | return {indicesAfterBranch, pathConditionOnExit}; |
2346 | 4.07k | } |
2347 | | |
2348 | | void SMTEncoder::initializeFunctionCallParameters(CallableDeclaration const& _function, std::vector<smtutil::Expression> const& _callArgs) |
2349 | 3.06k | { |
2350 | 3.06k | auto const& funParams = _function.parameters(); |
2351 | 3.06k | solAssert(funParams.size() == _callArgs.size(), ""); |
2352 | 4.86k | for (unsigned i = 0; i < funParams.size(); ++i) |
2353 | 1.79k | if (createVariable(*funParams[i])) |
2354 | 1.79k | { |
2355 | 1.79k | m_context.addAssertion(_callArgs[i] == m_context.newValue(*funParams[i])); |
2356 | 1.79k | if (funParams[i]->annotation().type->category() == Type::Category::Mapping) |
2357 | 4 | m_arrayAssignmentHappened = true; |
2358 | 1.79k | } |
2359 | | |
2360 | 3.06k | std::vector<VariableDeclaration const*> localVars; |
2361 | 3.06k | if (auto const* fun = dynamic_cast<FunctionDefinition const*>(&_function)) |
2362 | 2.08k | localVars = localVariablesIncludingModifiers(*fun, m_currentContract); |
2363 | 979 | else |
2364 | 979 | localVars = _function.localVariables(); |
2365 | 3.06k | for (auto const& variable: localVars) |
2366 | 508 | if (createVariable(*variable)) |
2367 | 508 | { |
2368 | 508 | m_context.newValue(*variable); |
2369 | 508 | m_context.setZeroValue(*variable); |
2370 | 508 | } |
2371 | | |
2372 | 3.06k | if (_function.returnParameterList()) |
2373 | 2.08k | for (auto const& retParam: _function.returnParameters()) |
2374 | 1.40k | if (createVariable(*retParam)) |
2375 | 1.40k | { |
2376 | 1.40k | m_context.newValue(*retParam); |
2377 | 1.40k | m_context.setZeroValue(*retParam); |
2378 | 1.40k | } |
2379 | 3.06k | } |
2380 | | |
2381 | | void SMTEncoder::createStateVariables(ContractDefinition const& _contract) |
2382 | 72.4k | { |
2383 | 72.4k | for (auto var: stateVariablesIncludingInheritedAndPrivate(_contract)) |
2384 | 38.7k | createVariable(*var); |
2385 | 72.4k | } |
2386 | | |
2387 | | void SMTEncoder::initializeStateVariables(ContractDefinition const& _contract) |
2388 | 15.8k | { |
2389 | 15.8k | for (auto var: _contract.stateVariables()) |
2390 | 7.61k | { |
2391 | 7.61k | solAssert(m_context.knownVariable(*var), ""); |
2392 | 7.61k | m_context.setZeroValue(*var); |
2393 | 7.61k | } |
2394 | | |
2395 | 15.8k | for (auto var: _contract.stateVariables()) |
2396 | 7.61k | if (var->value()) |
2397 | 2.32k | { |
2398 | 2.32k | var->value()->accept(*this); |
2399 | 2.32k | assignment(*var, *var->value()); |
2400 | 2.32k | } |
2401 | 15.8k | } |
2402 | | |
2403 | | void SMTEncoder::createLocalVariables(FunctionDefinition const& _function) |
2404 | 30.5k | { |
2405 | 30.5k | for (auto const& variable: localVariablesIncludingModifiers(_function, m_currentContract)) |
2406 | 8.33k | createVariable(*variable); |
2407 | | |
2408 | 30.5k | for (auto const& param: _function.parameters()) |
2409 | 14.5k | createVariable(*param); |
2410 | | |
2411 | 30.5k | if (_function.returnParameterList()) |
2412 | 30.5k | for (auto const& retParam: _function.returnParameters()) |
2413 | 16.3k | createVariable(*retParam); |
2414 | 30.5k | } |
2415 | | |
2416 | | void SMTEncoder::initializeLocalVariables(FunctionDefinition const& _function) |
2417 | 32.3k | { |
2418 | 32.3k | for (auto const& variable: localVariablesIncludingModifiers(_function, m_currentContract)) |
2419 | 8.45k | { |
2420 | 8.45k | solAssert(m_context.knownVariable(*variable), ""); |
2421 | 8.45k | m_context.setZeroValue(*variable); |
2422 | 8.45k | } |
2423 | | |
2424 | 32.3k | for (auto const& param: _function.parameters()) |
2425 | 15.6k | { |
2426 | 15.6k | solAssert(m_context.knownVariable(*param), ""); |
2427 | 15.6k | m_context.setUnknownValue(*param); |
2428 | 15.6k | } |
2429 | | |
2430 | 32.3k | if (_function.returnParameterList()) |
2431 | 32.3k | for (auto const& retParam: _function.returnParameters()) |
2432 | 17.7k | { |
2433 | 17.7k | solAssert(m_context.knownVariable(*retParam), ""); |
2434 | 17.7k | m_context.setZeroValue(*retParam); |
2435 | 17.7k | } |
2436 | 32.3k | } |
2437 | | |
2438 | | void SMTEncoder::resetStateVariables() |
2439 | 14.5k | { |
2440 | 44.0k | m_context.resetVariables([&](VariableDeclaration const& _variable) { return _variable.isStateVariable(); }); |
2441 | 14.5k | } |
2442 | | |
2443 | | void SMTEncoder::resetMemoryVariables() |
2444 | 1.08k | { |
2445 | 1.49k | m_context.resetVariables([&](VariableDeclaration const& _variable) { |
2446 | 1.49k | return _variable.referenceLocation() == VariableDeclaration::Location::Memory; |
2447 | 1.49k | }); |
2448 | 1.08k | } |
2449 | | |
2450 | | void SMTEncoder::resetStorageVariables() |
2451 | 2.98k | { |
2452 | 7.67k | m_context.resetVariables([&](VariableDeclaration const& _variable) { |
2453 | 7.67k | return _variable.referenceLocation() == VariableDeclaration::Location::Storage || _variable.isStateVariable(); |
2454 | 7.67k | }); |
2455 | 2.98k | } |
2456 | | |
2457 | | void SMTEncoder::resetBalances() |
2458 | 1.51k | { |
2459 | 1.51k | state().newBalances(); |
2460 | 1.51k | } |
2461 | | |
2462 | | void SMTEncoder::resetReferences(VariableDeclaration const& _varDecl) |
2463 | 15.6k | { |
2464 | 39.7k | m_context.resetVariables([&](VariableDeclaration const& _var) { |
2465 | 39.7k | if (_var == _varDecl) |
2466 | 15.6k | return false; |
2467 | | |
2468 | | // If both are state variables no need to clear knowledge. |
2469 | 24.0k | if (_var.isStateVariable() && _varDecl.isStateVariable()) |
2470 | 5.46k | return false; |
2471 | | |
2472 | 18.6k | return sameTypeOrSubtype(_var.type(), _varDecl.type()); |
2473 | 24.0k | }); |
2474 | 15.6k | } |
2475 | | |
2476 | | void SMTEncoder::resetReferences(Type const* _type) |
2477 | 14 | { |
2478 | 44 | m_context.resetVariables([&](VariableDeclaration const& _var) { |
2479 | 44 | return sameTypeOrSubtype(_var.type(), _type); |
2480 | 44 | }); |
2481 | 14 | } |
2482 | | |
2483 | | bool SMTEncoder::sameTypeOrSubtype(Type const* _a, Type const* _b) |
2484 | 18.6k | { |
2485 | 18.6k | bool foundSame = false; |
2486 | | |
2487 | 18.6k | solidity::util::BreadthFirstSearch<Type const*> bfs{{_a}}; |
2488 | 27.2k | bfs.run([&](auto _type, auto&& _addChild) { |
2489 | 27.2k | if (*typeWithoutPointer(_b) == *typeWithoutPointer(_type)) |
2490 | 1.91k | { |
2491 | 1.91k | foundSame = true; |
2492 | 1.91k | bfs.abort(); |
2493 | 1.91k | } |
2494 | 27.2k | if (auto const* mapType = dynamic_cast<MappingType const*>(_type)) |
2495 | 386 | _addChild(mapType->valueType()); |
2496 | 26.8k | else if (auto const* arrayType = dynamic_cast<ArrayType const*>(_type)) |
2497 | 5.37k | _addChild(arrayType->baseType()); |
2498 | 21.4k | else if (auto const* structType = dynamic_cast<StructType const*>(_type)) |
2499 | 1.91k | for (auto const& member: structType->nativeMembers(nullptr)) |
2500 | 4.00k | _addChild(member.type); |
2501 | 27.2k | }); |
2502 | | |
2503 | 18.6k | return foundSame; |
2504 | 18.6k | } |
2505 | | |
2506 | | bool SMTEncoder::isSupportedType(Type const& _type) const |
2507 | 57.0k | { |
2508 | 57.0k | return smt::isSupportedType(*underlyingType(&_type)); |
2509 | 57.0k | } |
2510 | | |
2511 | | Type const* SMTEncoder::typeWithoutPointer(Type const* _type) |
2512 | 54.4k | { |
2513 | 54.4k | if (auto refType = dynamic_cast<ReferenceType const*>(_type)) |
2514 | 32.7k | return TypeProvider::withLocationIfReference(refType->location(), _type); |
2515 | 21.7k | return _type; |
2516 | 54.4k | } |
2517 | | |
2518 | | void SMTEncoder::mergeVariables(smtutil::Expression const& _condition, VariableIndices const& _indicesEndTrue, VariableIndices const& _indicesEndFalse) |
2519 | 5.66k | { |
2520 | 5.66k | for (auto const& entry: _indicesEndTrue) |
2521 | 23.7k | { |
2522 | 23.7k | VariableDeclaration const* var = entry.first; |
2523 | 23.7k | auto trueIndex = entry.second; |
2524 | 23.7k | if (_indicesEndFalse.count(var) && _indicesEndFalse.at(var) != trueIndex) |
2525 | 6.02k | { |
2526 | 6.02k | m_context.addAssertion(m_context.newValue(*var) == smtutil::Expression::ite( |
2527 | 6.02k | _condition, |
2528 | 6.02k | valueAtIndex(*var, trueIndex), |
2529 | 6.02k | valueAtIndex(*var, _indicesEndFalse.at(var))) |
2530 | 6.02k | ); |
2531 | 6.02k | } |
2532 | 23.7k | } |
2533 | 5.66k | } |
2534 | | |
2535 | | smtutil::Expression SMTEncoder::currentValue(VariableDeclaration const& _decl) const |
2536 | 151k | { |
2537 | 151k | solAssert(m_context.knownVariable(_decl), ""); |
2538 | 151k | return m_context.variable(_decl)->currentValue(); |
2539 | 151k | } |
2540 | | |
2541 | | smtutil::Expression SMTEncoder::valueAtIndex(VariableDeclaration const& _decl, unsigned _index) const |
2542 | 41.5k | { |
2543 | 41.5k | solAssert(m_context.knownVariable(_decl), ""); |
2544 | 41.5k | return m_context.variable(_decl)->valueAtIndex(_index); |
2545 | 41.5k | } |
2546 | | |
2547 | | bool SMTEncoder::createVariable(VariableDeclaration const& _varDecl) |
2548 | 114k | { |
2549 | 114k | if (m_context.knownVariable(_varDecl)) |
2550 | 88.3k | return true; |
2551 | 25.7k | bool abstract = m_context.createVariable(_varDecl); |
2552 | 25.7k | if (abstract) |
2553 | 17 | { |
2554 | 17 | m_unsupportedErrors.warning( |
2555 | 17 | 8115_error, |
2556 | 17 | _varDecl.location(), |
2557 | 17 | "Assertion checker does not yet support the type of this variable." |
2558 | 17 | ); |
2559 | 17 | return false; |
2560 | 17 | } |
2561 | 25.6k | return true; |
2562 | 25.7k | } |
2563 | | |
2564 | | smtutil::Expression SMTEncoder::expr(Expression const& _e, Type const* _targetType) |
2565 | 698k | { |
2566 | 698k | if (!m_context.knownExpression(_e)) |
2567 | 101 | { |
2568 | 101 | m_unsupportedErrors.warning(6031_error, _e.location(), "Internal error: Expression undefined for SMT solver." ); |
2569 | 101 | createExpr(_e); |
2570 | 101 | } |
2571 | | |
2572 | 698k | return m_context.expression(_e)->currentValue(underlyingType(_targetType)); |
2573 | 698k | } |
2574 | | |
2575 | | void SMTEncoder::createExpr(Expression const& _e) |
2576 | 413k | { |
2577 | 413k | bool abstract = m_context.createExpression(_e); |
2578 | 413k | if (abstract) |
2579 | 498 | m_unsupportedErrors.warning( |
2580 | 498 | 8364_error, |
2581 | 498 | _e.location(), |
2582 | 498 | "Assertion checker does not yet implement type " + _e.annotation().type->toString() |
2583 | 498 | ); |
2584 | 413k | } |
2585 | | |
2586 | | void SMTEncoder::defineExpr(Expression const& _e, smtutil::Expression _value) |
2587 | 271k | { |
2588 | 271k | auto type = _e.annotation().type; |
2589 | 271k | createExpr(_e); |
2590 | 271k | solAssert(_value.sort->kind != smtutil::Kind::Function, "Equality operator applied to type that is not fully supported"); |
2591 | 271k | if (!smt::isInaccessibleDynamic(*type)) |
2592 | 271k | m_context.addAssertion(expr(_e) == _value); |
2593 | | |
2594 | 271k | if (m_checked && smt::isNumber(*type)) |
2595 | 171k | m_context.addAssertion(smtutil::Expression::implies( |
2596 | 171k | currentPathConditions(), |
2597 | 171k | smt::symbolicUnknownConstraints(expr(_e), type) |
2598 | 171k | )); |
2599 | 271k | } |
2600 | | |
2601 | | void SMTEncoder::defineExpr(Expression const& _e, std::vector<std::optional<smtutil::Expression>> const& _values) |
2602 | 12.0k | { |
2603 | 12.0k | if (_values.size() == 1 && _values.front()) |
2604 | 6.43k | { |
2605 | 6.43k | defineExpr(_e, *_values.front()); |
2606 | 6.43k | return; |
2607 | 6.43k | } |
2608 | 5.64k | auto const& symbTuple = std::dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_e)); |
2609 | 5.64k | solAssert(symbTuple, ""); |
2610 | 5.64k | symbTuple->increaseIndex(); |
2611 | 5.64k | auto const& symbComponents = symbTuple->components(); |
2612 | 5.64k | solAssert(symbComponents.size() == _values.size(), ""); |
2613 | 5.64k | auto tupleType = dynamic_cast<TupleType const*>(_e.annotation().type); |
2614 | 5.64k | solAssert(tupleType, ""); |
2615 | 5.64k | solAssert(tupleType->components().size() == symbComponents.size(), ""); |
2616 | 15.4k | for (unsigned i = 0; i < symbComponents.size(); ++i) |
2617 | 9.83k | if (_values[i] && !smt::isInaccessibleDynamic(*tupleType->components()[i])) |
2618 | 9.68k | m_context.addAssertion(symbTuple->component(i) == *_values[i]); |
2619 | 5.64k | } |
2620 | | |
2621 | | void SMTEncoder::popPathCondition() |
2622 | 23.3k | { |
2623 | 23.3k | solAssert(m_pathConditions.size() > 0, "Cannot pop path condition, empty."); |
2624 | 23.3k | m_pathConditions.pop_back(); |
2625 | 23.3k | } |
2626 | | |
2627 | | void SMTEncoder::pushPathCondition(smtutil::Expression const& _e) |
2628 | 23.3k | { |
2629 | 23.3k | m_pathConditions.push_back(currentPathConditions() && _e); |
2630 | 23.3k | } |
2631 | | |
2632 | | void SMTEncoder::setPathCondition(smtutil::Expression const& _e) |
2633 | 6.45k | { |
2634 | 6.45k | if (m_pathConditions.empty()) |
2635 | 0 | m_pathConditions.push_back(_e); |
2636 | 6.45k | else |
2637 | 6.45k | m_pathConditions.back() = _e; |
2638 | 6.45k | } |
2639 | | |
2640 | | smtutil::Expression SMTEncoder::currentPathConditions() |
2641 | 267k | { |
2642 | 267k | if (m_pathConditions.empty()) |
2643 | 139k | return smtutil::Expression(true); |
2644 | 128k | return m_pathConditions.back(); |
2645 | 267k | } |
2646 | | |
2647 | | SecondarySourceLocation SMTEncoder::callStackMessage(std::vector<CallStackEntry> const& _callStack) |
2648 | 0 | { |
2649 | 0 | SecondarySourceLocation callStackLocation; |
2650 | 0 | solAssert(!_callStack.empty(), ""); |
2651 | 0 | callStackLocation.append("Callstack:", SourceLocation()); |
2652 | 0 | for (auto const& call: _callStack | ranges::views::reverse) |
2653 | 0 | if (call.second) |
2654 | 0 | callStackLocation.append("", call.second->location()); |
2655 | 0 | return callStackLocation; |
2656 | 0 | } |
2657 | | |
2658 | | std::pair<CallableDeclaration const*, ASTNode const*> SMTEncoder::popCallStack() |
2659 | 46.9k | { |
2660 | 46.9k | solAssert(!m_callStack.empty(), ""); |
2661 | 46.9k | auto lastCalled = m_callStack.back(); |
2662 | 46.9k | m_callStack.pop_back(); |
2663 | 46.9k | return lastCalled; |
2664 | 46.9k | } |
2665 | | |
2666 | | void SMTEncoder::pushCallStack(CallStackEntry _entry) |
2667 | 46.9k | { |
2668 | 46.9k | m_callStack.push_back(_entry); |
2669 | 46.9k | } |
2670 | | |
2671 | | void SMTEncoder::addPathImpliedExpression(smtutil::Expression const& _e) |
2672 | 1.00k | { |
2673 | 1.00k | m_context.addAssertion(smtutil::Expression::implies(currentPathConditions(), _e)); |
2674 | 1.00k | } |
2675 | | |
2676 | | bool SMTEncoder::isRootFunction() |
2677 | 18.6k | { |
2678 | 18.6k | return m_callStack.size() == 1; |
2679 | 18.6k | } |
2680 | | |
2681 | | bool SMTEncoder::visitedFunction(FunctionDefinition const* _funDef) |
2682 | 2.13k | { |
2683 | 2.13k | for (auto const& call: m_callStack) |
2684 | 2.62k | if (call.first == _funDef) |
2685 | 296 | return true; |
2686 | 1.83k | return false; |
2687 | 2.13k | } |
2688 | | |
2689 | | ContractDefinition const* SMTEncoder::currentScopeContract() |
2690 | 21.7k | { |
2691 | 21.7k | for (auto&& f: m_callStack | ranges::views::reverse | ranges::views::keys) |
2692 | 21.5k | if (auto fun = dynamic_cast<FunctionDefinition const*>(f)) |
2693 | 21.0k | return fun->annotation().contract; |
2694 | 704 | return nullptr; |
2695 | 21.7k | } |
2696 | | |
2697 | | SMTEncoder::VariableIndices SMTEncoder::copyVariableIndices() |
2698 | 16.8k | { |
2699 | 16.8k | VariableIndices indices; |
2700 | 16.8k | for (auto const& var: m_context.variables()) |
2701 | 64.8k | indices.emplace(var.first, var.second->index()); |
2702 | 16.8k | return indices; |
2703 | 16.8k | } |
2704 | | |
2705 | | void SMTEncoder::resetVariableIndices(VariableIndices const& _indices) |
2706 | 5.41k | { |
2707 | 5.41k | for (auto const& var: _indices) |
2708 | 16.8k | m_context.variable(*var.first)->setIndex(var.second); |
2709 | 5.41k | } |
2710 | | |
2711 | | void SMTEncoder::clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function) |
2712 | 205k | { |
2713 | 205k | solAssert(_contract, ""); |
2714 | 205k | for (auto var: stateVariablesIncludingInheritedAndPrivate(*_contract)) |
2715 | 109k | m_context.variable(*var)->resetIndex(); |
2716 | 205k | if (_function) |
2717 | 105k | { |
2718 | 105k | for (auto const& var: _function->parameters() + _function->returnParameters()) |
2719 | 112k | m_context.variable(*var)->resetIndex(); |
2720 | 105k | for (auto const& var: localVariablesIncludingModifiers(*_function, _contract)) |
2721 | 41.7k | m_context.variable(*var)->resetIndex(); |
2722 | 105k | } |
2723 | 205k | state().reset(); |
2724 | 205k | } |
2725 | | |
2726 | | Expression const* SMTEncoder::leftmostBase(IndexAccess const& _indexAccess) |
2727 | 42 | { |
2728 | 42 | Expression const* base = &_indexAccess.baseExpression(); |
2729 | 42 | while (auto access = dynamic_cast<IndexAccess const*>(base)) |
2730 | 0 | base = &access->baseExpression(); |
2731 | 42 | return base; |
2732 | 42 | } |
2733 | | |
2734 | | Type const* SMTEncoder::keyType(Type const* _type) |
2735 | 34.5k | { |
2736 | 34.5k | if (auto const* mappingType = dynamic_cast<MappingType const*>(_type)) |
2737 | 3.54k | return mappingType->keyType(); |
2738 | 31.0k | if ( |
2739 | 31.0k | dynamic_cast<ArrayType const*>(_type) || |
2740 | 172 | dynamic_cast<ArraySliceType const*>(_type) |
2741 | 31.0k | ) |
2742 | 31.0k | return TypeProvider::uint256(); |
2743 | 0 | else |
2744 | 31.0k | solAssert(false, ""); |
2745 | 31.0k | } |
2746 | | |
2747 | | Expression const& SMTEncoder::innermostTuple(Expression const& _expr) |
2748 | 35.5k | { |
2749 | 35.5k | auto const* tuple = dynamic_cast<TupleExpression const*>(&_expr); |
2750 | 35.5k | if (!tuple || tuple->isInlineArray()) |
2751 | 34.1k | return _expr; |
2752 | | |
2753 | 1.35k | Expression const* expr = tuple; |
2754 | 2.34k | while (tuple && !tuple->isInlineArray() && tuple->components().size() == 1) |
2755 | 983 | { |
2756 | 983 | expr = tuple->components().front().get(); |
2757 | 983 | tuple = dynamic_cast<TupleExpression const*>(expr); |
2758 | 983 | } |
2759 | 1.35k | solAssert(expr, ""); |
2760 | 1.35k | return *expr; |
2761 | 35.5k | } |
2762 | | |
2763 | | Type const* SMTEncoder::underlyingType(Type const* _type) |
2764 | 756k | { |
2765 | 756k | if (auto userType = dynamic_cast<UserDefinedValueType const*>(_type)) |
2766 | 790 | _type = &userType->underlyingType(); |
2767 | 756k | return _type; |
2768 | 756k | } |
2769 | | |
2770 | | TypePointers SMTEncoder::replaceUserTypes(TypePointers const& _types) |
2771 | 11.8k | { |
2772 | 11.8k | return applyMap(_types, [](auto _type) { |
2773 | 3.37k | if (auto userType = dynamic_cast<UserDefinedValueType const*>(_type)) |
2774 | 14 | return &userType->underlyingType(); |
2775 | 3.36k | return _type; |
2776 | 3.37k | }); |
2777 | 11.8k | } |
2778 | | |
2779 | | std::pair<Expression const*, FunctionCallOptions const*> SMTEncoder::functionCallExpression(FunctionCall const& _funCall) |
2780 | 23.8k | { |
2781 | 23.8k | Expression const* callExpr = &innermostTuple(_funCall.expression()); |
2782 | 23.8k | auto const* callOptions = dynamic_cast<FunctionCallOptions const*>(callExpr); |
2783 | 23.8k | if (callOptions) |
2784 | 461 | callExpr = &callOptions->expression(); |
2785 | | |
2786 | 23.8k | return {callExpr, callOptions}; |
2787 | 23.8k | } |
2788 | | |
2789 | | Expression const* SMTEncoder::cleanExpression(Expression const& _expr) |
2790 | 28.7k | { |
2791 | 28.7k | auto const* expr = &_expr; |
2792 | 28.7k | if (auto const* tuple = dynamic_cast<TupleExpression const*>(expr)) |
2793 | 58 | return cleanExpression(innermostTuple(*tuple)); |
2794 | 28.6k | if (auto const* functionCall = dynamic_cast<FunctionCall const*>(expr)) |
2795 | 198 | if (*functionCall->annotation().kind == FunctionCallKind::TypeConversion) |
2796 | 12 | { |
2797 | 12 | auto typeType = dynamic_cast<TypeType const*>(functionCall->expression().annotation().type); |
2798 | 12 | solAssert(typeType, ""); |
2799 | 12 | if (auto const* arrayType = dynamic_cast<ArrayType const*>(typeType->actualType())) |
2800 | 12 | if (arrayType->isByteArrayOrString()) |
2801 | 12 | { |
2802 | | // this is a cast to `bytes` |
2803 | 12 | solAssert(functionCall->arguments().size() == 1, ""); |
2804 | 12 | Expression const& arg = *functionCall->arguments()[0]; |
2805 | 12 | if ( |
2806 | 12 | auto const* argArrayType = dynamic_cast<ArrayType const*>(arg.annotation().type); |
2807 | 12 | argArrayType && argArrayType->isByteArrayOrString() |
2808 | 12 | ) |
2809 | 12 | return cleanExpression(arg); |
2810 | 12 | } |
2811 | 12 | } |
2812 | 28.6k | solAssert(expr, ""); |
2813 | 28.6k | return expr; |
2814 | 28.6k | } |
2815 | | |
2816 | | Declaration const* SMTEncoder::expressionToDeclaration(Expression const& _expr) const |
2817 | 3.30k | { |
2818 | 3.30k | if (auto const* identifier = dynamic_cast<Identifier const*>(&_expr)) |
2819 | 2.25k | return identifier->annotation().referencedDeclaration; |
2820 | 1.04k | if (auto const* outerMemberAccess = dynamic_cast<MemberAccess const*>(&_expr)) |
2821 | 98 | return outerMemberAccess->annotation().referencedDeclaration; |
2822 | 946 | return nullptr; |
2823 | 1.04k | } |
2824 | | |
2825 | | VariableDeclaration const* SMTEncoder::identifierToVariable(Expression const& _expr) const |
2826 | 144k | { |
2827 | | // We do not use `expressionToDeclaration` here because we are not interested in |
2828 | | // struct.field, for example. |
2829 | 144k | if (auto const* identifier = dynamic_cast<Identifier const*>(&_expr)) |
2830 | 136k | if (auto const* varDecl = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration)) |
2831 | 120k | { |
2832 | 120k | solAssert(m_context.knownVariable(*varDecl), ""); |
2833 | 120k | return varDecl; |
2834 | 120k | } |
2835 | | // But we are interested in "contract.var", because that is the same as just "var". |
2836 | 24.1k | if (auto const* memberAccess = dynamic_cast<MemberAccess const*>(&_expr)) |
2837 | 3.03k | if (dynamic_cast<ContractDefinition const*>(expressionToDeclaration( |
2838 | 3.03k | *cleanExpression(memberAccess->expression()) |
2839 | 3.03k | ))) |
2840 | 55 | if (auto const* varDecl = dynamic_cast<VariableDeclaration const*>(memberAccess->annotation().referencedDeclaration)) |
2841 | 55 | { |
2842 | 55 | solAssert(m_context.knownVariable(*varDecl), ""); |
2843 | 55 | return varDecl; |
2844 | 55 | } |
2845 | | |
2846 | 24.0k | return nullptr; |
2847 | 24.1k | } |
2848 | | |
2849 | | MemberAccess const* SMTEncoder::isEmptyPush(Expression const& _expr) const |
2850 | 0 | { |
2851 | 0 | if ( |
2852 | 0 | auto const* funCall = dynamic_cast<FunctionCall const*>(&_expr); |
2853 | 0 | funCall && funCall->arguments().empty() |
2854 | 0 | ) |
2855 | 0 | { |
2856 | 0 | auto const& funType = dynamic_cast<FunctionType const&>(*funCall->expression().annotation().type); |
2857 | 0 | if (funType.kind() == FunctionType::Kind::ArrayPush) |
2858 | 0 | return &dynamic_cast<MemberAccess const&>(funCall->expression()); |
2859 | 0 | } |
2860 | 0 | return nullptr; |
2861 | 0 | } |
2862 | | |
2863 | | smtutil::Expression SMTEncoder::contractAddressValue(FunctionCall const& _f) |
2864 | 2.11k | { |
2865 | 2.11k | FunctionType const& funType = dynamic_cast<FunctionType const&>(*_f.expression().annotation().type); |
2866 | 2.11k | if (funType.kind() == FunctionType::Kind::Internal) |
2867 | 1.71k | return state().thisAddress(); |
2868 | 409 | auto [funExpr, funOptions] = functionCallExpression(_f); |
2869 | 409 | if (MemberAccess const* callBase = dynamic_cast<MemberAccess const*>(funExpr)) |
2870 | 409 | return expr(callBase->expression()); |
2871 | 0 | smtAssert(false, "Unexpected function call type encountered while getting contract address!"); |
2872 | 0 | } |
2873 | | |
2874 | 4.11k | VariableDeclaration const* SMTEncoder::publicGetter(Expression const& _expr) const { |
2875 | 4.11k | if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_expr)) |
2876 | 3.38k | if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(memberAccess->annotation().referencedDeclaration)) |
2877 | 1.18k | return variableDeclaration->isStateVariable() ? variableDeclaration : nullptr; |
2878 | 2.92k | return nullptr; |
2879 | 4.11k | } |
2880 | | |
2881 | 1.79k | bool SMTEncoder::isExternalCallToThis(Expression const* _expr) { |
2882 | 1.79k | auto memberAccess = dynamic_cast<MemberAccess const*>(_expr); |
2883 | 1.79k | if (!memberAccess) |
2884 | 82 | return false; |
2885 | | |
2886 | 1.70k | auto identifier = dynamic_cast<Identifier const*>(&memberAccess->expression()); |
2887 | 1.70k | return identifier && |
2888 | 1.40k | identifier->name() == "this" && |
2889 | 1.16k | identifier->annotation().referencedDeclaration && |
2890 | 1.16k | dynamic_cast<MagicVariableDeclaration const*>(identifier->annotation().referencedDeclaration) |
2891 | 1.79k | ; |
2892 | 1.79k | } |
2893 | | |
2894 | | std::string SMTEncoder::extraComment() |
2895 | 3.04k | { |
2896 | 3.04k | std::string extra; |
2897 | 3.04k | if (m_arrayAssignmentHappened) |
2898 | 46 | extra += |
2899 | 46 | "\nNote that array aliasing is not supported," |
2900 | 46 | " therefore all mapping information is erased after" |
2901 | 46 | " a mapping local variable/parameter is assigned.\n" |
2902 | 46 | "You can re-introduce information using require()."; |
2903 | 3.04k | return extra; |
2904 | 3.04k | } |
2905 | | |
2906 | | FunctionDefinition const* SMTEncoder::functionCallToDefinition( |
2907 | | FunctionCall const& _funCall, |
2908 | | ContractDefinition const* _scopeContract, |
2909 | | ContractDefinition const* _contextContract |
2910 | | ) |
2911 | 21.7k | { |
2912 | 21.7k | if (*_funCall.annotation().kind != FunctionCallKind::FunctionCall) |
2913 | 0 | return {}; |
2914 | | |
2915 | 21.7k | auto [calledExpr, callOptions] = functionCallExpression(_funCall); |
2916 | | |
2917 | 21.7k | if (TupleExpression const* fun = dynamic_cast<TupleExpression const*>(calledExpr)) |
2918 | 4 | { |
2919 | 4 | solAssert(fun->components().size() == 1, ""); |
2920 | 4 | calledExpr = &innermostTuple(*calledExpr); |
2921 | 4 | } |
2922 | | |
2923 | 21.7k | auto resolveVirtual = [&](auto const* _ref) -> FunctionDefinition const* { |
2924 | 20.0k | VirtualLookup lookup = *_ref->annotation().requiredLookup; |
2925 | 20.0k | solAssert(_contextContract || lookup == VirtualLookup::Static, "No contract context provided for function lookup resolution!"); |
2926 | 20.0k | auto funDef = dynamic_cast<FunctionDefinition const*>(_ref->annotation().referencedDeclaration); |
2927 | 20.0k | if (!funDef) |
2928 | 8.01k | return funDef; |
2929 | 12.0k | switch (lookup) |
2930 | 12.0k | { |
2931 | 6.75k | case VirtualLookup::Virtual: |
2932 | 6.75k | return &(funDef->resolveVirtual(*_contextContract)); |
2933 | 462 | case VirtualLookup::Super: |
2934 | 462 | { |
2935 | 462 | solAssert(_scopeContract, ""); |
2936 | 462 | auto super = _scopeContract->superContract(*_contextContract); |
2937 | 462 | solAssert(super, "Super contract not available."); |
2938 | 462 | return &funDef->resolveVirtual(*_contextContract, super); |
2939 | 0 | } |
2940 | 4.83k | case VirtualLookup::Static: |
2941 | 4.83k | return funDef; |
2942 | 12.0k | } |
2943 | 0 | solAssert(false, ""); |
2944 | 0 | }; SMTEncoder.cpp:solidity::frontend::FunctionDefinition const* solidity::frontend::SMTEncoder::functionCallToDefinition(solidity::frontend::FunctionCall const&, solidity::frontend::ContractDefinition const*, solidity::frontend::ContractDefinition const*)::$_0::operator()<solidity::frontend::Identifier>(solidity::frontend::Identifier const*) const Line | Count | Source | 2923 | 11.1k | auto resolveVirtual = [&](auto const* _ref) -> FunctionDefinition const* { | 2924 | 11.1k | VirtualLookup lookup = *_ref->annotation().requiredLookup; | 2925 | 11.1k | solAssert(_contextContract || lookup == VirtualLookup::Static, "No contract context provided for function lookup resolution!"); | 2926 | 11.1k | auto funDef = dynamic_cast<FunctionDefinition const*>(_ref->annotation().referencedDeclaration); | 2927 | 11.1k | if (!funDef) | 2928 | 4.43k | return funDef; | 2929 | 6.75k | switch (lookup) | 2930 | 6.75k | { | 2931 | 6.75k | case VirtualLookup::Virtual: | 2932 | 6.75k | return &(funDef->resolveVirtual(*_contextContract)); | 2933 | 0 | case VirtualLookup::Super: | 2934 | 0 | { | 2935 | 0 | solAssert(_scopeContract, ""); | 2936 | 0 | auto super = _scopeContract->superContract(*_contextContract); | 2937 | 0 | solAssert(super, "Super contract not available."); | 2938 | 0 | return &funDef->resolveVirtual(*_contextContract, super); | 2939 | 0 | } | 2940 | 0 | case VirtualLookup::Static: | 2941 | 0 | return funDef; | 2942 | 6.75k | } | 2943 | 0 | solAssert(false, ""); | 2944 | 0 | }; |
SMTEncoder.cpp:solidity::frontend::FunctionDefinition const* solidity::frontend::SMTEncoder::functionCallToDefinition(solidity::frontend::FunctionCall const&, solidity::frontend::ContractDefinition const*, solidity::frontend::ContractDefinition const*)::$_0::operator()<solidity::frontend::MemberAccess>(solidity::frontend::MemberAccess const*) const Line | Count | Source | 2923 | 8.87k | auto resolveVirtual = [&](auto const* _ref) -> FunctionDefinition const* { | 2924 | 8.87k | VirtualLookup lookup = *_ref->annotation().requiredLookup; | 2925 | 8.87k | solAssert(_contextContract || lookup == VirtualLookup::Static, "No contract context provided for function lookup resolution!"); | 2926 | 8.87k | auto funDef = dynamic_cast<FunctionDefinition const*>(_ref->annotation().referencedDeclaration); | 2927 | 8.87k | if (!funDef) | 2928 | 3.58k | return funDef; | 2929 | 5.29k | switch (lookup) | 2930 | 5.29k | { | 2931 | 0 | case VirtualLookup::Virtual: | 2932 | 0 | return &(funDef->resolveVirtual(*_contextContract)); | 2933 | 462 | case VirtualLookup::Super: | 2934 | 462 | { | 2935 | 462 | solAssert(_scopeContract, ""); | 2936 | 462 | auto super = _scopeContract->superContract(*_contextContract); | 2937 | 462 | solAssert(super, "Super contract not available."); | 2938 | 462 | return &funDef->resolveVirtual(*_contextContract, super); | 2939 | 0 | } | 2940 | 4.83k | case VirtualLookup::Static: | 2941 | 4.83k | return funDef; | 2942 | 5.29k | } | 2943 | 0 | solAssert(false, ""); | 2944 | 0 | }; |
|
2945 | | |
2946 | 21.7k | if (Identifier const* fun = dynamic_cast<Identifier const*>(calledExpr)) |
2947 | 11.1k | return resolveVirtual(fun); |
2948 | 10.5k | else if (MemberAccess const* fun = dynamic_cast<MemberAccess const*>(calledExpr)) |
2949 | 8.87k | return resolveVirtual(fun); |
2950 | | |
2951 | 1.67k | return {}; |
2952 | 21.7k | } |
2953 | | |
2954 | | std::vector<VariableDeclaration const*> SMTEncoder::stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract) |
2955 | 1.54M | { |
2956 | 1.54M | return fold( |
2957 | 1.54M | _contract.annotation().linearizedBaseContracts, |
2958 | 1.54M | std::vector<VariableDeclaration const*>{}, |
2959 | 1.79M | [](auto&& _acc, auto _contract) { return _acc + _contract->stateVariables(); } |
2960 | 1.54M | ); |
2961 | 1.54M | } |
2962 | | |
2963 | | std::vector<VariableDeclaration const*> SMTEncoder::stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function) |
2964 | 80 | { |
2965 | 80 | if (auto contract = dynamic_cast<ContractDefinition const*>(_function.scope())) |
2966 | 78 | return stateVariablesIncludingInheritedAndPrivate(*contract); |
2967 | 2 | return {}; |
2968 | 80 | } |
2969 | | |
2970 | | std::vector<VariableDeclaration const*> SMTEncoder::localVariablesIncludingModifiers(FunctionDefinition const& _function, ContractDefinition const* _contract) |
2971 | 501k | { |
2972 | 501k | return _function.localVariables() + tryCatchVariables(_function) + modifiersVariables(_function, _contract); |
2973 | 501k | } |
2974 | | |
2975 | | std::vector<VariableDeclaration const*> SMTEncoder::tryCatchVariables(FunctionDefinition const& _function) |
2976 | 501k | { |
2977 | 501k | struct TryCatchVarsVisitor : public ASTConstVisitor |
2978 | 501k | { |
2979 | 501k | bool visit(TryCatchClause const& _catchClause) override |
2980 | 501k | { |
2981 | 19.4k | if (_catchClause.parameters()) |
2982 | 3.80k | { |
2983 | 3.80k | auto const& params = _catchClause.parameters()->parameters(); |
2984 | 3.80k | for (auto param: params) |
2985 | 4.52k | vars.push_back(param.get()); |
2986 | 3.80k | } |
2987 | | |
2988 | 19.4k | return true; |
2989 | 19.4k | } |
2990 | | |
2991 | 501k | std::vector<VariableDeclaration const*> vars; |
2992 | 501k | } tryCatchVarsVisitor; |
2993 | 501k | _function.accept(tryCatchVarsVisitor); |
2994 | 501k | return tryCatchVarsVisitor.vars; |
2995 | 501k | } |
2996 | | |
2997 | | std::vector<VariableDeclaration const*> SMTEncoder::modifiersVariables(FunctionDefinition const& _function, ContractDefinition const* _contract) |
2998 | 501k | { |
2999 | 501k | struct BlockVars: ASTConstVisitor |
3000 | 501k | { |
3001 | 501k | BlockVars(Block const& _block) { _block.accept(*this); } |
3002 | 501k | void endVisit(VariableDeclaration const& _var) { vars.push_back(&_var); } |
3003 | 501k | std::vector<VariableDeclaration const*> vars; |
3004 | 501k | }; |
3005 | | |
3006 | 501k | std::vector<VariableDeclaration const*> vars; |
3007 | 501k | std::set<ModifierDefinition const*> visited; |
3008 | 501k | for (auto invok: _function.modifiers()) |
3009 | 40.3k | { |
3010 | 40.3k | if (!invok) |
3011 | 0 | continue; |
3012 | 40.3k | auto const* modifier = resolveModifierInvocation(*invok, _contract); |
3013 | 40.3k | if (!modifier || visited.count(modifier)) |
3014 | 21.6k | continue; |
3015 | | |
3016 | 18.7k | visited.insert(modifier); |
3017 | 18.7k | if (modifier->isImplemented()) |
3018 | 18.6k | { |
3019 | 18.6k | vars += applyMap(modifier->parameters(), [](auto _var) { return _var.get(); }); |
3020 | 18.6k | vars += BlockVars(modifier->body()).vars; |
3021 | 18.6k | } |
3022 | 18.7k | } |
3023 | 501k | return vars; |
3024 | 501k | } |
3025 | | |
3026 | | ModifierDefinition const* SMTEncoder::resolveModifierInvocation(ModifierInvocation const& _invocation, ContractDefinition const* _contract) |
3027 | 41.3k | { |
3028 | 41.3k | auto const* modifier = dynamic_cast<ModifierDefinition const*>(_invocation.name().annotation().referencedDeclaration); |
3029 | 41.3k | if (modifier) |
3030 | 35.1k | { |
3031 | 35.1k | VirtualLookup lookup = *_invocation.name().annotation().requiredLookup; |
3032 | 35.1k | solAssert(lookup == VirtualLookup::Virtual || lookup == VirtualLookup::Static, ""); |
3033 | 35.1k | solAssert(_contract || lookup == VirtualLookup::Static, "No contract context provided for modifier lookup resolution!"); |
3034 | 35.1k | if (lookup == VirtualLookup::Virtual) |
3035 | 34.9k | modifier = &modifier->resolveVirtual(*_contract); |
3036 | 35.1k | } |
3037 | 41.3k | return modifier; |
3038 | 41.3k | } |
3039 | | |
3040 | | std::set<FunctionDefinition const*, ASTNode::CompareByID> const& SMTEncoder::contractFunctions(ContractDefinition const& _contract) |
3041 | 54.5k | { |
3042 | 54.5k | if (!m_contractFunctions.count(&_contract)) |
3043 | 28.0k | { |
3044 | 28.0k | auto const& functions = _contract.definedFunctions(); |
3045 | 28.0k | std::set<FunctionDefinition const*, ASTNode::CompareByID> resolvedFunctions(begin(functions), end(functions)); |
3046 | 28.0k | for (auto const* base: _contract.annotation().linearizedBaseContracts) |
3047 | 31.7k | { |
3048 | 31.7k | if (base == &_contract) |
3049 | 28.0k | continue; |
3050 | 3.65k | for (auto const* baseFunction: base->definedFunctions()) |
3051 | 3.27k | { |
3052 | 3.27k | if (baseFunction->isConstructor()) // We don't want to include constructors of parent contracts |
3053 | 1.01k | continue; |
3054 | 2.25k | bool overridden = false; |
3055 | 2.25k | for (auto const* function: resolvedFunctions) |
3056 | 2.46k | if ( |
3057 | 2.46k | function->name() == baseFunction->name() && |
3058 | 1.10k | function->kind() == baseFunction->kind() && |
3059 | 1.09k | FunctionType(*function).asExternallyCallableFunction(false)-> |
3060 | 1.09k | hasEqualParameterTypes(*FunctionType(*baseFunction).asExternallyCallableFunction(false)) |
3061 | 2.46k | ) |
3062 | 1.00k | { |
3063 | 1.00k | overridden = true; |
3064 | 1.00k | break; |
3065 | 1.00k | } |
3066 | 2.25k | if (!overridden) |
3067 | 1.24k | resolvedFunctions.insert(baseFunction); |
3068 | 2.25k | } |
3069 | 3.65k | } |
3070 | 28.0k | m_contractFunctions.emplace(&_contract, std::move(resolvedFunctions)); |
3071 | 28.0k | } |
3072 | 54.5k | return m_contractFunctions.at(&_contract); |
3073 | 54.5k | } |
3074 | | |
3075 | | std::set<FunctionDefinition const*, ASTNode::CompareByID> const& SMTEncoder::contractFunctionsWithoutVirtual(ContractDefinition const& _contract) |
3076 | 42.2k | { |
3077 | 42.2k | if (!m_contractFunctionsWithoutVirtual.count(&_contract)) |
3078 | 28.0k | { |
3079 | 28.0k | auto allFunctions = contractFunctions(_contract); |
3080 | 28.0k | for (auto const* base: _contract.annotation().linearizedBaseContracts) |
3081 | 31.7k | for (auto const* baseFun: base->definedFunctions()) |
3082 | 30.1k | allFunctions.insert(baseFun); |
3083 | | |
3084 | 28.0k | m_contractFunctionsWithoutVirtual.emplace(&_contract, std::move(allFunctions)); |
3085 | | |
3086 | 28.0k | } |
3087 | 42.2k | return m_contractFunctionsWithoutVirtual.at(&_contract); |
3088 | 42.2k | } |
3089 | | |
3090 | | std::map<ContractDefinition const*, std::vector<ASTPointer<frontend::Expression>>> SMTEncoder::baseArguments(ContractDefinition const& _contract) |
3091 | 13.8k | { |
3092 | 13.8k | std::map<ContractDefinition const*, std::vector<ASTPointer<Expression>>> baseArgs; |
3093 | | |
3094 | 13.8k | for (auto contract: _contract.annotation().linearizedBaseContracts) |
3095 | 15.4k | { |
3096 | | /// Collect base contracts and potential constructor arguments. |
3097 | 15.4k | for (auto specifier: contract->baseContracts()) |
3098 | 1.75k | { |
3099 | 1.75k | solAssert(specifier, ""); |
3100 | 1.75k | auto const& base = dynamic_cast<ContractDefinition const&>(*specifier->name().annotation().referencedDeclaration); |
3101 | 1.75k | if (auto args = specifier->arguments()) |
3102 | 50 | baseArgs[&base] = *args; |
3103 | 1.75k | } |
3104 | | /// Collect base constructor arguments given as constructor modifiers. |
3105 | 15.4k | if (auto constructor = contract->constructor()) |
3106 | 2.03k | for (auto mod: constructor->modifiers()) |
3107 | 362 | { |
3108 | 362 | auto decl = mod->name().annotation().referencedDeclaration; |
3109 | 362 | if (auto base = dynamic_cast<ContractDefinition const*>(decl)) |
3110 | 261 | { |
3111 | 261 | solAssert(!baseArgs.count(base), ""); |
3112 | 261 | if (auto args = mod->arguments()) |
3113 | 261 | baseArgs[base] = *args; |
3114 | 261 | } |
3115 | 362 | } |
3116 | 15.4k | } |
3117 | | |
3118 | 13.8k | return baseArgs; |
3119 | 13.8k | } |
3120 | | |
3121 | | RationalNumberType const* SMTEncoder::isConstant(Expression const& _expr) |
3122 | 83.1k | { |
3123 | 83.1k | if (auto type = dynamic_cast<RationalNumberType const*>(_expr.annotation().type)) |
3124 | 11.8k | return type; |
3125 | | |
3126 | 71.3k | if ( |
3127 | 71.3k | auto typedValue = ConstantEvaluator::tryEvaluate(_expr); |
3128 | 71.3k | std::holds_alternative<rational>(typedValue.value) |
3129 | 71.3k | ) |
3130 | 1.26k | return TypeProvider::rationalNumber(std::get<rational>(typedValue.value)); |
3131 | | |
3132 | 70.0k | return nullptr; |
3133 | 71.3k | } |
3134 | | |
3135 | | std::set<FunctionCall const*, ASTCompareByID<FunctionCall>> SMTEncoder::collectABICalls(ASTNode const* _node) |
3136 | 28.0k | { |
3137 | 28.0k | struct ABIFunctions: public ASTConstVisitor |
3138 | 28.0k | { |
3139 | 28.0k | ABIFunctions(ASTNode const* _node) { _node->accept(*this); } |
3140 | 28.0k | void endVisit(FunctionCall const& _funCall) |
3141 | 28.0k | { |
3142 | 26.3k | if (*_funCall.annotation().kind == FunctionCallKind::FunctionCall) |
3143 | 20.9k | switch (dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type).kind()) |
3144 | 20.9k | { |
3145 | 322 | case FunctionType::Kind::ABIEncode: |
3146 | 472 | case FunctionType::Kind::ABIEncodePacked: |
3147 | 516 | case FunctionType::Kind::ABIEncodeWithSelector: |
3148 | 554 | case FunctionType::Kind::ABIEncodeCall: |
3149 | 594 | case FunctionType::Kind::ABIEncodeWithSignature: |
3150 | 842 | case FunctionType::Kind::ABIDecode: |
3151 | 842 | abiCalls.insert(&_funCall); |
3152 | 842 | break; |
3153 | 20.0k | default: {} |
3154 | 20.9k | } |
3155 | 26.3k | } |
3156 | | |
3157 | 28.0k | std::set<FunctionCall const*, ASTCompareByID<FunctionCall>> abiCalls; |
3158 | 28.0k | }; |
3159 | | |
3160 | 28.0k | return ABIFunctions(_node).abiCalls; |
3161 | 28.0k | } |
3162 | | |
3163 | | std::set<FunctionCall const*, ASTCompareByID<FunctionCall>> SMTEncoder::collectBytesConcatCalls(ASTNode const* _node) |
3164 | 28.0k | { |
3165 | 28.0k | struct BytesConcatFunctions: public ASTConstVisitor |
3166 | 28.0k | { |
3167 | 28.0k | BytesConcatFunctions(ASTNode const* _node) { _node->accept(*this); } |
3168 | 28.0k | void endVisit(FunctionCall const& _funCall) |
3169 | 28.0k | { |
3170 | 26.3k | if (*_funCall.annotation().kind == FunctionCallKind::FunctionCall) |
3171 | 20.9k | switch (dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type).kind()) |
3172 | 20.9k | { |
3173 | 134 | case FunctionType::Kind::BytesConcat: |
3174 | 134 | bytesConcatCalls.insert(&_funCall); |
3175 | 134 | break; |
3176 | 20.7k | default: {} |
3177 | 20.9k | } |
3178 | 26.3k | } |
3179 | | |
3180 | 28.0k | std::set<FunctionCall const*, ASTCompareByID<FunctionCall>> bytesConcatCalls; |
3181 | 28.0k | }; |
3182 | | |
3183 | 28.0k | return BytesConcatFunctions(_node).bytesConcatCalls; |
3184 | 28.0k | } |
3185 | | |
3186 | | std::set<SourceUnit const*, ASTNode::CompareByID> SMTEncoder::sourceDependencies(SourceUnit const& _source) |
3187 | 26.8k | { |
3188 | 26.8k | std::set<SourceUnit const*, ASTNode::CompareByID> sources; |
3189 | 26.8k | sources.insert(&_source); |
3190 | 26.8k | for (auto const& source: _source.referencedSourceUnits(true)) |
3191 | 1.33k | sources.insert(source); |
3192 | 26.8k | return sources; |
3193 | 26.8k | } |
3194 | | |
3195 | | void SMTEncoder::createReturnedExpressions(FunctionDefinition const* _funDef, Expression const& _callStackExpr) |
3196 | 12.6k | { |
3197 | 12.6k | if (!_funDef) |
3198 | 8.02k | return; |
3199 | | |
3200 | 4.62k | auto const& returnParams = _funDef->returnParameters(); |
3201 | 4.62k | for (auto param: returnParams) |
3202 | 3.71k | createVariable(*param); |
3203 | 4.62k | auto returnValues = applyMap(returnParams, [this](auto const& param) -> std::optional<smtutil::Expression> { |
3204 | 3.71k | solAssert(param && m_context.knownVariable(*param), ""); |
3205 | 3.71k | return currentValue(*param); |
3206 | 3.71k | }); |
3207 | 4.62k | defineExpr(_callStackExpr, returnValues); |
3208 | 4.62k | } |
3209 | | |
3210 | | std::vector<smtutil::Expression> SMTEncoder::symbolicArguments( |
3211 | | std::vector<ASTPointer<VariableDeclaration>> const& _funParameters, |
3212 | | std::vector<Expression const*> const& _arguments, |
3213 | | std::optional<Expression const*> _boundArgumentCall |
3214 | | ) |
3215 | 3.89k | { |
3216 | 3.89k | std::vector<smtutil::Expression> args; |
3217 | 3.89k | unsigned firstParam = 0; |
3218 | 3.89k | if (_boundArgumentCall) |
3219 | 228 | { |
3220 | 228 | Expression const& calledExpr = innermostTuple(*_boundArgumentCall.value()); |
3221 | 228 | auto const& attachedFunction = dynamic_cast<MemberAccess const*>(&calledExpr); |
3222 | 228 | solAssert(attachedFunction, ""); |
3223 | 228 | args.push_back(expr(attachedFunction->expression(), _funParameters.front()->type())); |
3224 | 228 | firstParam = 1; |
3225 | 228 | } |
3226 | | |
3227 | 3.89k | solAssert((_arguments.size() + firstParam) == _funParameters.size(), ""); |
3228 | | |
3229 | 5.94k | for (unsigned i = 0; i < _arguments.size(); ++i) |
3230 | 2.05k | args.push_back(expr(*_arguments.at(i), _funParameters.at(i + firstParam)->type())); |
3231 | | |
3232 | 3.89k | return args; |
3233 | 3.89k | } |
3234 | | |
3235 | | smtutil::Expression SMTEncoder::constantExpr(Expression const& _expr, VariableDeclaration const& _var) |
3236 | 1.04k | { |
3237 | 1.04k | if (RationalNumberType const* rationalType = isConstant(_expr)) |
3238 | 856 | { |
3239 | 856 | if (rationalType->isNegative()) |
3240 | 2 | return smtutil::Expression(u2s(rationalType->literalValue(nullptr))); |
3241 | 854 | else |
3242 | 854 | return smtutil::Expression(rationalType->literalValue(nullptr)); |
3243 | 856 | } |
3244 | 188 | else |
3245 | 188 | { |
3246 | 188 | solAssert(_var.value(), ""); |
3247 | 188 | _var.value()->accept(*this); |
3248 | 188 | return expr(*_var.value(), _expr.annotation().type); |
3249 | 188 | } |
3250 | 0 | solAssert(false, ""); |
3251 | 0 | } |
3252 | | |
3253 | | void SMTEncoder::collectFreeFunctions(std::set<SourceUnit const*, ASTNode::CompareByID> const& _sources) |
3254 | 13.4k | { |
3255 | 13.4k | for (auto source: _sources) |
3256 | 14.0k | for (auto node: source->nodes()) |
3257 | 34.2k | if (auto function = dynamic_cast<FunctionDefinition const*>(node.get())) |
3258 | 1.17k | m_freeFunctions.insert(function); |
3259 | 33.0k | else if ( |
3260 | 33.0k | auto contract = dynamic_cast<ContractDefinition const*>(node.get()); |
3261 | 33.0k | contract && contract->isLibrary() |
3262 | 33.0k | ) |
3263 | 468 | for (auto function: contract->definedFunctions()) |
3264 | | // We need to add public library functions too because they can be called |
3265 | | // internally by internal library functions that are considered free functions. |
3266 | 440 | m_freeFunctions.insert(function); |
3267 | 13.4k | } |
3268 | | |
3269 | | void SMTEncoder::createFreeConstants(std::set<SourceUnit const*, ASTNode::CompareByID> const& _sources) |
3270 | 26.8k | { |
3271 | 26.8k | for (auto source: _sources) |
3272 | 28.0k | for (auto node: source->nodes()) |
3273 | 68.5k | if (auto var = dynamic_cast<VariableDeclaration const*>(node.get())) |
3274 | 394 | createVariable(*var); |
3275 | 68.1k | else if ( |
3276 | 68.1k | auto contract = dynamic_cast<ContractDefinition const*>(node.get()); |
3277 | 68.1k | contract && contract->isLibrary() |
3278 | 68.1k | ) |
3279 | 936 | for (auto var: contract->stateVariables()) |
3280 | 72 | { |
3281 | 72 | solAssert(var->isConstant(), ""); |
3282 | 72 | createVariable(*var); |
3283 | 72 | } |
3284 | 26.8k | } |
3285 | | |
3286 | | void SMTEncoder::createStateVariables(std::set<SourceUnit const*, ASTNode::CompareByID> const& _sources) |
3287 | 13.4k | { |
3288 | 13.4k | for (auto const& source: _sources) |
3289 | 14.0k | for (auto const& node: source->nodes()) |
3290 | 34.2k | if (auto contract = dynamic_cast<ContractDefinition const*>(node.get())) |
3291 | 14.3k | createStateVariables(*contract); |
3292 | 13.4k | } |
3293 | | |
3294 | | smt::SymbolicState& SMTEncoder::state() |
3295 | 1.32M | { |
3296 | 1.32M | return m_context.state(); |
3297 | 1.32M | } |
3298 | | |
3299 | | smtutil::Expression SMTEncoder::createSelectExpressionForFunction( |
3300 | | smtutil::Expression symbFunction, |
3301 | | std::vector<frontend::ASTPointer<frontend::Expression const>> const& args, |
3302 | | frontend::TypePointers const& inTypes, |
3303 | | unsigned long argsActualLength |
3304 | | ) |
3305 | 1.00k | { |
3306 | 1.00k | solAssert(argsActualLength <= args.size() && inTypes.size() == argsActualLength); |
3307 | 1.00k | if (inTypes.size() == 1) |
3308 | 595 | { |
3309 | 595 | smtutil::Expression arg = expr(*args.at(0), inTypes.at(0)); |
3310 | 595 | return smtutil::Expression::select(symbFunction, arg); |
3311 | 595 | } |
3312 | | |
3313 | 405 | std::vector<smtutil::Expression> symbArgs; |
3314 | 1.36k | for (unsigned i = 0; i < argsActualLength; ++i) |
3315 | 962 | if (args.at(i)) |
3316 | 962 | symbArgs.emplace_back(expr(*args.at(i), inTypes.at(i))); |
3317 | | |
3318 | 405 | auto inputSort = dynamic_cast<smtutil::ArraySort&>(*symbFunction.sort).domain; |
3319 | 405 | smtutil::Expression arg = smtutil::Expression::tuple_constructor( |
3320 | 405 | smtutil::Expression(std::make_shared<smtutil::SortSort>(inputSort), ""), |
3321 | 405 | symbArgs |
3322 | 405 | ); |
3323 | 405 | return smtutil::Expression::select(symbFunction, arg); |
3324 | 1.00k | } |