/src/solidity/libsolidity/formal/BMC.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/BMC.h> |
20 | | |
21 | | #include <libsolidity/formal/Cvc5SMTLib2Interface.h> |
22 | | #include <libsolidity/formal/SymbolicTypes.h> |
23 | | #include <libsolidity/formal/Z3SMTLib2Interface.h> |
24 | | |
25 | | #include <libsmtutil/SMTLib2Interface.h> |
26 | | #include <libsmtutil/SMTPortfolio.h> |
27 | | |
28 | | #include <liblangutil/CharStream.h> |
29 | | #include <liblangutil/CharStreamProvider.h> |
30 | | |
31 | | #include <utility> |
32 | | |
33 | | using namespace solidity; |
34 | | using namespace solidity::util; |
35 | | using namespace solidity::langutil; |
36 | | using namespace solidity::frontend; |
37 | | using namespace solidity::frontend::smt; |
38 | | using namespace solidity::smtutil; |
39 | | |
40 | | BMC::BMC( |
41 | | smt::EncodingContext& _context, |
42 | | UniqueErrorReporter& _errorReporter, |
43 | | UniqueErrorReporter& _unsupportedErrorReporter, |
44 | | ErrorReporter& _provedSafeReporter, |
45 | | std::map<h256, std::string> const& _smtlib2Responses, |
46 | | ReadCallback::Callback const& _smtCallback, |
47 | | ModelCheckerSettings _settings, |
48 | | CharStreamProvider const& _charStreamProvider |
49 | | ): |
50 | 12.5k | SMTEncoder(_context, _settings, _errorReporter, _unsupportedErrorReporter, _provedSafeReporter, _charStreamProvider) |
51 | 12.5k | { |
52 | 12.5k | std::vector<std::unique_ptr<BMCSolverInterface>> solvers; |
53 | 12.5k | if (_settings.solvers.smtlib2) |
54 | 11.9k | solvers.emplace_back(std::make_unique<SMTLib2Interface>(_smtlib2Responses, _smtCallback, _settings.timeout)); |
55 | 12.5k | if (_settings.solvers.cvc5) |
56 | 0 | solvers.emplace_back(std::make_unique<Cvc5SMTLib2Interface>(_smtCallback, _settings.timeout)); |
57 | 12.5k | if (_settings.solvers.z3 ) |
58 | 669 | solvers.emplace_back(std::make_unique<Z3SMTLib2Interface>(_smtCallback, _settings.timeout)); |
59 | 12.5k | m_interface = std::make_unique<SMTPortfolio>(std::move(solvers), _settings.timeout); |
60 | 12.5k | } |
61 | | |
62 | | void BMC::analyze(SourceUnit const& _source, std::map<ASTNode const*, std::set<VerificationTargetType>, smt::EncodingContext::IdCompare> _solvedTargets) |
63 | 13.4k | { |
64 | | // At this point every enabled solver is available. |
65 | 13.4k | if (!m_settings.solvers.cvc5 && !m_settings.solvers.smtlib2 && !m_settings.solvers.z3) |
66 | 0 | { |
67 | 0 | m_errorReporter.warning( |
68 | 0 | 7710_error, |
69 | 0 | SourceLocation(), |
70 | 0 | "BMC analysis was not possible since no SMT solver was found and enabled." |
71 | 0 | " The accepted solvers for BMC are cvc5 and z3." |
72 | 0 | ); |
73 | 0 | return; |
74 | 0 | } |
75 | | |
76 | 13.4k | SMTEncoder::resetSourceAnalysis(); |
77 | | |
78 | 13.4k | state().prepareForSourceUnit(_source, false); |
79 | 13.4k | m_solvedTargets = std::move(_solvedTargets); |
80 | 13.4k | m_context.setSolver(m_interface.get()); |
81 | 13.4k | m_context.reset(); |
82 | 13.4k | m_context.setAssertionAccumulation(true); |
83 | 13.4k | auto const& sources = sourceDependencies(_source); |
84 | 13.4k | createFreeConstants(sources); |
85 | 13.4k | createStateVariables(sources); |
86 | 13.4k | m_unprovedAmt = 0; |
87 | | |
88 | 13.4k | _source.accept(*this); |
89 | | |
90 | 13.4k | if (m_unprovedAmt > 0 && !m_settings.showUnproved) |
91 | 1.74k | m_errorReporter.warning( |
92 | 1.74k | 2788_error, |
93 | 1.74k | {}, |
94 | 1.74k | "BMC: " + |
95 | 1.74k | std::to_string(m_unprovedAmt) + |
96 | 1.74k | " verification condition(s) could not be proved." + |
97 | 1.74k | " Enable the model checker option \"show unproved\" to see all of them." + |
98 | 1.74k | " Consider choosing a specific contract to be verified in order to reduce the solving problems." + |
99 | 1.74k | " Consider increasing the timeout per query." |
100 | 1.74k | ); |
101 | | |
102 | 13.4k | if (!m_settings.showProvedSafe && !m_safeTargets.empty()) |
103 | 0 | { |
104 | 0 | std::size_t provedSafeNum = 0; |
105 | 0 | for (auto&& [_, targets]: m_safeTargets) |
106 | 0 | provedSafeNum += targets.size(); |
107 | 0 | m_errorReporter.info( |
108 | 0 | 6002_error, |
109 | 0 | "BMC: " + |
110 | 0 | std::to_string(provedSafeNum) + |
111 | 0 | " verification condition(s) proved safe!" + |
112 | 0 | " Enable the model checker option \"show proved safe\" to see all of them." |
113 | 0 | ); |
114 | 0 | } |
115 | 13.4k | else if (m_settings.showProvedSafe) |
116 | 0 | for (auto const& [node, targets]: m_safeTargets) |
117 | 0 | for (auto const& target: targets) |
118 | 0 | m_provedSafeReporter.info( |
119 | 0 | 2961_error, |
120 | 0 | node->location(), |
121 | 0 | "BMC: " + |
122 | 0 | targetDescription(target) + |
123 | 0 | " check is safe!" |
124 | 0 | ); |
125 | | |
126 | | // If this check is true, Z3 and cvc5 are not available |
127 | | // and the query answers were not provided, since SMTPortfolio |
128 | | // guarantees that SmtLib2Interface is the first solver, if enabled. |
129 | 13.4k | if ( |
130 | 13.4k | !m_interface->unhandledQueries().empty() && |
131 | 2.17k | m_interface->solvers() == 1 && |
132 | 2.17k | m_settings.solvers.smtlib2 |
133 | 13.4k | ) |
134 | 2.17k | m_errorReporter.warning( |
135 | 2.17k | 8084_error, |
136 | 2.17k | SourceLocation(), |
137 | 2.17k | "BMC analysis was not possible. No SMT solver (Z3 or cvc5) was available." |
138 | 2.17k | " None of the installed solvers was enabled." |
139 | 2.17k | ); |
140 | 13.4k | } |
141 | | |
142 | | bool BMC::shouldInlineFunctionCall( |
143 | | FunctionCall const& _funCall, |
144 | | ContractDefinition const* _scopeContract, |
145 | | ContractDefinition const* _contextContract |
146 | | ) |
147 | 5.52k | { |
148 | 5.52k | auto funDef = functionCallToDefinition(_funCall, _scopeContract, _contextContract); |
149 | 5.52k | if (!funDef || !funDef->isImplemented()) |
150 | 930 | return false; |
151 | | |
152 | 4.59k | FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type); |
153 | 4.59k | if (funType.kind() == FunctionType::Kind::External) |
154 | 880 | return isExternalCallToThis(&_funCall.expression()); |
155 | 3.71k | else if (funType.kind() != FunctionType::Kind::Internal) |
156 | 175 | return false; |
157 | | |
158 | 3.54k | return true; |
159 | 4.59k | } |
160 | | |
161 | | /// AST visitors. |
162 | | |
163 | | bool BMC::visit(ContractDefinition const& _contract) |
164 | 14.0k | { |
165 | | // Raises UnimplementedFeatureError in the presence of transient storage variables |
166 | 14.0k | TransientDataLocationChecker checker(_contract); |
167 | | |
168 | 14.0k | initContract(_contract); |
169 | | |
170 | 14.0k | SMTEncoder::visit(_contract); |
171 | | |
172 | 14.0k | return false; |
173 | 14.0k | } |
174 | | |
175 | | void BMC::endVisit(ContractDefinition const& _contract) |
176 | 14.0k | { |
177 | 14.0k | if (auto constructor = _contract.constructor()) |
178 | 1.56k | constructor->accept(*this); |
179 | 12.4k | else |
180 | 12.4k | { |
181 | | /// Visiting implicit constructor - we need a dummy callstack frame |
182 | 12.4k | pushCallStack({nullptr, nullptr}); |
183 | 12.4k | inlineConstructorHierarchy(_contract); |
184 | 12.4k | popCallStack(); |
185 | | /// Check targets created by state variable initialization. |
186 | 12.4k | checkVerificationTargets(); |
187 | 12.4k | m_verificationTargets.clear(); |
188 | 12.4k | } |
189 | | |
190 | 14.0k | SMTEncoder::endVisit(_contract); |
191 | 14.0k | } |
192 | | |
193 | | bool BMC::visit(FunctionDefinition const& _function) |
194 | 17.8k | { |
195 | | // Free functions need to be visited in the context of a contract. |
196 | 17.8k | if (!m_currentContract) |
197 | 888 | return false; |
198 | | |
199 | 16.9k | auto contract = dynamic_cast<ContractDefinition const*>(_function.scope()); |
200 | 16.9k | auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts; |
201 | 16.9k | if (contract && find(hierarchy.begin(), hierarchy.end(), contract) == hierarchy.end()) |
202 | 183 | createStateVariables(*contract); |
203 | | |
204 | 16.9k | if (m_callStack.empty()) |
205 | 14.5k | { |
206 | 14.5k | reset(); |
207 | 14.5k | initFunction(_function); |
208 | 14.5k | if (_function.isConstructor() || _function.isPublic()) |
209 | 13.5k | m_context.addAssertion(state().txTypeConstraints() && state().txFunctionConstraints(_function)); |
210 | 14.5k | resetStateVariables(); |
211 | 14.5k | } |
212 | | |
213 | 16.9k | if (_function.isConstructor()) |
214 | 2.07k | { |
215 | 2.07k | solAssert(contract, ""); |
216 | 2.07k | inlineConstructorHierarchy(*contract); |
217 | 2.07k | } |
218 | | |
219 | | /// Already visits the children. |
220 | 16.9k | SMTEncoder::visit(_function); |
221 | | |
222 | 16.9k | return false; |
223 | 17.8k | } |
224 | | |
225 | | void BMC::endVisit(FunctionDefinition const& _function) |
226 | 17.8k | { |
227 | | // Free functions need to be visited in the context of a contract. |
228 | 17.8k | if (!m_currentContract) |
229 | 888 | return; |
230 | | |
231 | 16.9k | if (isRootFunction()) |
232 | 14.5k | { |
233 | 14.5k | checkVerificationTargets(); |
234 | 14.5k | m_verificationTargets.clear(); |
235 | 14.5k | m_pathConditions.clear(); |
236 | 14.5k | } |
237 | | |
238 | 16.9k | SMTEncoder::endVisit(_function); |
239 | 16.9k | } |
240 | | |
241 | | bool BMC::visit(IfStatement const& _node) |
242 | 735 | { |
243 | 735 | auto indicesBeforePush = copyVariableIndices(); |
244 | | // This check needs to be done in its own context otherwise |
245 | | // constraints from the If body might influence it. |
246 | 735 | m_context.pushSolver(); |
247 | 735 | _node.condition().accept(*this); |
248 | | |
249 | 735 | checkIfConditionIsConstant(_node.condition()); |
250 | | |
251 | 735 | m_context.popSolver(); |
252 | 735 | resetVariableIndices(std::move(indicesBeforePush)); |
253 | | |
254 | 735 | _node.condition().accept(*this); |
255 | 735 | auto conditionExpr = expr(_node.condition()); |
256 | | // visit true branch |
257 | 735 | auto [indicesEndTrue, trueEndPathCondition] = visitBranch(&_node.trueStatement(), conditionExpr); |
258 | | |
259 | | // visit false branch |
260 | 735 | decltype(indicesEndTrue) indicesEndFalse; |
261 | 735 | auto falseEndPathCondition = currentPathConditions() && !conditionExpr; |
262 | 735 | if (_node.falseStatement()) |
263 | 106 | std::tie(indicesEndFalse, falseEndPathCondition) = visitBranch(_node.falseStatement(), !conditionExpr); |
264 | 629 | else |
265 | 629 | indicesEndFalse = copyVariableIndices(); |
266 | | |
267 | | // merge the information from branches |
268 | 735 | setPathCondition(trueEndPathCondition || falseEndPathCondition); |
269 | 735 | mergeVariables(expr(_node.condition()), indicesEndTrue, indicesEndFalse); |
270 | | |
271 | 735 | return false; |
272 | 735 | } |
273 | | |
274 | | bool BMC::visit(Conditional const& _op) |
275 | 604 | { |
276 | 604 | auto indicesBeforePush = copyVariableIndices(); |
277 | 604 | m_context.pushSolver(); |
278 | 604 | _op.condition().accept(*this); |
279 | 604 | checkIfConditionIsConstant(_op.condition()); |
280 | 604 | m_context.popSolver(); |
281 | 604 | resetVariableIndices(std::move(indicesBeforePush)); |
282 | | |
283 | 604 | SMTEncoder::visit(_op); |
284 | | |
285 | 604 | return false; |
286 | 604 | } |
287 | | |
288 | | // Unrolls while or do-while loop |
289 | | bool BMC::visit(WhileStatement const& _node) |
290 | 178 | { |
291 | 178 | unsigned int bmcLoopIterations = m_settings.bmcLoopIterations.value_or(1); |
292 | 178 | smtutil::Expression broke(false); |
293 | 178 | smtutil::Expression loopCondition(true); |
294 | 178 | if (_node.isDoWhile()) |
295 | 38 | { |
296 | 76 | for (unsigned int i = 0; i < bmcLoopIterations; ++i) |
297 | 38 | { |
298 | 38 | m_loopCheckpoints.emplace(); |
299 | | |
300 | 38 | auto indicesBefore = copyVariableIndices(); |
301 | 38 | _node.body().accept(*this); |
302 | | |
303 | 38 | auto brokeInCurrentIteration = mergeVariablesFromLoopCheckpoints(); |
304 | | |
305 | 38 | auto indicesBreak = copyVariableIndices(); |
306 | 38 | _node.condition().accept(*this); |
307 | 38 | mergeVariables( |
308 | 38 | !brokeInCurrentIteration, |
309 | 38 | copyVariableIndices(), |
310 | 38 | indicesBreak |
311 | 38 | ); |
312 | | |
313 | 38 | mergeVariables( |
314 | 38 | broke || !loopCondition, |
315 | 38 | indicesBefore, |
316 | 38 | copyVariableIndices() |
317 | 38 | ); |
318 | 38 | loopCondition = loopCondition && expr(_node.condition()); |
319 | 38 | broke = broke || brokeInCurrentIteration; |
320 | 38 | m_loopCheckpoints.pop(); |
321 | 38 | } |
322 | 38 | if (bmcLoopIterations > 0) |
323 | 38 | m_context.addAssertion(!loopCondition || broke); |
324 | 38 | } |
325 | 140 | else { |
326 | 140 | smtutil::Expression loopConditionOnPreviousIterations(true); |
327 | 280 | for (unsigned int i = 0; i < bmcLoopIterations; ++i) |
328 | 140 | { |
329 | 140 | m_loopCheckpoints.emplace(); |
330 | 140 | auto indicesBefore = copyVariableIndices(); |
331 | 140 | _node.condition().accept(*this); |
332 | 140 | loopCondition = expr(_node.condition()); |
333 | | |
334 | 140 | auto indicesAfterCondition = copyVariableIndices(); |
335 | | |
336 | 140 | pushPathCondition(loopCondition); |
337 | 140 | _node.body().accept(*this); |
338 | 140 | popPathCondition(); |
339 | | |
340 | 140 | auto brokeInCurrentIteration = mergeVariablesFromLoopCheckpoints(); |
341 | | |
342 | | // merges indices modified when accepting loop condition that no longer holds |
343 | 140 | mergeVariables( |
344 | 140 | !loopCondition, |
345 | 140 | indicesAfterCondition, |
346 | 140 | copyVariableIndices() |
347 | 140 | ); |
348 | | |
349 | | // handles breaks in previous iterations |
350 | | // breaks in current iterations are handled when traversing loop checkpoints |
351 | | // handles case when the loop condition no longer holds but bmc loop iterations still unrolls the loop |
352 | 140 | mergeVariables( |
353 | 140 | broke || !loopConditionOnPreviousIterations, |
354 | 140 | indicesBefore, |
355 | 140 | copyVariableIndices() |
356 | 140 | ); |
357 | 140 | m_loopCheckpoints.pop(); |
358 | 140 | broke = broke || brokeInCurrentIteration; |
359 | 140 | loopConditionOnPreviousIterations = loopConditionOnPreviousIterations && loopCondition; |
360 | 140 | } |
361 | 140 | if (bmcLoopIterations > 0) |
362 | 140 | { |
363 | | //after loop iterations are done, we check the loop condition last final time |
364 | 140 | auto indices = copyVariableIndices(); |
365 | 140 | m_loopCheckpoints.emplace(); |
366 | 140 | _node.condition().accept(*this); |
367 | 140 | m_loopCheckpoints.pop(); |
368 | 140 | loopCondition = expr(_node.condition()); |
369 | | // assert that the loop is complete |
370 | 140 | m_context.addAssertion(!loopCondition || broke || !loopConditionOnPreviousIterations); |
371 | 140 | mergeVariables( |
372 | 140 | broke || !loopConditionOnPreviousIterations, |
373 | 140 | indices, |
374 | 140 | copyVariableIndices() |
375 | 140 | ); |
376 | 140 | } |
377 | 140 | } |
378 | 178 | m_loopExecutionHappened = true; |
379 | 178 | return false; |
380 | 178 | } |
381 | | |
382 | | // Unrolls for loop |
383 | | bool BMC::visit(ForStatement const& _node) |
384 | 614 | { |
385 | 614 | if (_node.initializationExpression()) |
386 | 557 | _node.initializationExpression()->accept(*this); |
387 | | |
388 | 614 | smtutil::Expression broke(false); |
389 | 614 | smtutil::Expression forCondition(true); |
390 | 614 | smtutil::Expression forConditionOnPreviousIterations(true); |
391 | 614 | unsigned int bmcLoopIterations = m_settings.bmcLoopIterations.value_or(1); |
392 | 1.22k | for (unsigned int i = 0; i < bmcLoopIterations; ++i) |
393 | 614 | { |
394 | 614 | auto indicesBefore = copyVariableIndices(); |
395 | 614 | m_loopCheckpoints.emplace(); |
396 | 614 | if (_node.condition()) |
397 | 583 | { |
398 | 583 | _node.condition()->accept(*this); |
399 | | // values in loop condition might change during loop iteration |
400 | 583 | forCondition = expr(*_node.condition()); |
401 | 583 | } |
402 | 614 | auto indicesAfterCondition = copyVariableIndices(); |
403 | | |
404 | 614 | pushPathCondition(forCondition); |
405 | 614 | _node.body().accept(*this); |
406 | | |
407 | 614 | auto brokeInCurrentIteration = mergeVariablesFromLoopCheckpoints(); |
408 | | |
409 | | // accept loop expression if there was no break |
410 | 614 | if (_node.loopExpression()) |
411 | 559 | { |
412 | 559 | auto indicesBreak = copyVariableIndices(); |
413 | 559 | _node.loopExpression()->accept(*this); |
414 | 559 | mergeVariables( |
415 | 559 | !brokeInCurrentIteration, |
416 | 559 | copyVariableIndices(), |
417 | 559 | indicesBreak |
418 | 559 | ); |
419 | 559 | } |
420 | 614 | popPathCondition(); |
421 | | |
422 | | // merges indices modified when accepting loop condition that does no longer hold |
423 | 614 | mergeVariables( |
424 | 614 | !forCondition, |
425 | 614 | indicesAfterCondition, |
426 | 614 | copyVariableIndices() |
427 | 614 | ); |
428 | | |
429 | | // handles breaks in previous iterations |
430 | | // breaks in current iterations are handled when traversing loop checkpoints |
431 | | // handles case when the loop condition no longer holds but bmc loop iterations still unrolls the loop |
432 | 614 | mergeVariables( |
433 | 614 | broke || !forConditionOnPreviousIterations, |
434 | 614 | indicesBefore, |
435 | 614 | copyVariableIndices() |
436 | 614 | ); |
437 | 614 | m_loopCheckpoints.pop(); |
438 | 614 | broke = broke || brokeInCurrentIteration; |
439 | 614 | forConditionOnPreviousIterations = forConditionOnPreviousIterations && forCondition; |
440 | 614 | } |
441 | 614 | if (bmcLoopIterations > 0) |
442 | 614 | { |
443 | | //after loop iterations are done, we check the loop condition last final time |
444 | 614 | auto indices = copyVariableIndices(); |
445 | 614 | if (_node.condition()) |
446 | 583 | { |
447 | 583 | m_loopCheckpoints.emplace(); |
448 | 583 | _node.condition()->accept(*this); |
449 | 583 | forCondition = expr(*_node.condition()); |
450 | 583 | m_loopCheckpoints.pop(); |
451 | 583 | } |
452 | | // assert that the loop is complete |
453 | 614 | m_context.addAssertion(!forCondition || broke || !forConditionOnPreviousIterations); |
454 | 614 | mergeVariables( |
455 | 614 | broke || !forConditionOnPreviousIterations, |
456 | 614 | indices, |
457 | 614 | copyVariableIndices() |
458 | 614 | ); |
459 | 614 | } |
460 | 614 | m_loopExecutionHappened = true; |
461 | 614 | return false; |
462 | 614 | } |
463 | | |
464 | | // merges variables based on loop control statements |
465 | | // returns expression indicating whether there was a break in current loop unroll iteration |
466 | | smtutil::Expression BMC::mergeVariablesFromLoopCheckpoints() |
467 | 792 | { |
468 | 792 | smtutil::Expression continues(false); |
469 | 792 | smtutil::Expression brokeInCurrentIteration(false); |
470 | 792 | for (auto const& loopControl: m_loopCheckpoints.top()) |
471 | 153 | { |
472 | | // use SSAs associated with this break statement only if |
473 | | // loop didn't break or continue earlier in the iteration |
474 | | // loop condition is included in break path conditions |
475 | 153 | mergeVariables( |
476 | 153 | !brokeInCurrentIteration && !continues && loopControl.pathConditions, |
477 | 153 | loopControl.variableIndices, |
478 | 153 | copyVariableIndices() |
479 | 153 | ); |
480 | 153 | if (loopControl.kind == LoopControlKind::Break) |
481 | 84 | brokeInCurrentIteration = |
482 | 84 | brokeInCurrentIteration || loopControl.pathConditions; |
483 | 69 | else if (loopControl.kind == LoopControlKind::Continue) |
484 | 69 | continues = continues || loopControl.pathConditions; |
485 | 153 | } |
486 | 792 | return brokeInCurrentIteration; |
487 | 792 | } |
488 | | |
489 | | bool BMC::visit(TryStatement const& _tryStatement) |
490 | 148 | { |
491 | 148 | FunctionCall const* externalCall = dynamic_cast<FunctionCall const*>(&_tryStatement.externalCall()); |
492 | 148 | solAssert(externalCall && externalCall->annotation().tryCall, ""); |
493 | | |
494 | 148 | externalCall->accept(*this); |
495 | 148 | if (_tryStatement.successClause()->parameters()) |
496 | 36 | expressionToTupleAssignment(_tryStatement.successClause()->parameters()->parameters(), *externalCall); |
497 | | |
498 | 148 | smtutil::Expression clauseId = m_context.newVariable("clause_choice_" + std::to_string(m_context.newUniqueId()), smtutil::SortProvider::uintSort); |
499 | 148 | auto const& clauses = _tryStatement.clauses(); |
500 | 148 | m_context.addAssertion(clauseId >= 0 && clauseId < clauses.size()); |
501 | 148 | solAssert(clauses[0].get() == _tryStatement.successClause(), "First clause of TryStatement should be the success clause"); |
502 | 148 | std::vector<std::pair<VariableIndices, smtutil::Expression>> clausesVisitResults; |
503 | 450 | for (size_t i = 0; i < clauses.size(); ++i) |
504 | 302 | clausesVisitResults.push_back(visitBranch(clauses[i].get())); |
505 | | |
506 | | // merge the information from all clauses |
507 | 148 | smtutil::Expression pathCondition = clausesVisitResults.front().second; |
508 | 148 | auto currentIndices = clausesVisitResults[0].first; |
509 | 302 | for (size_t i = 1; i < clauses.size(); ++i) |
510 | 154 | { |
511 | 154 | mergeVariables(clauseId == i, clausesVisitResults[i].first, currentIndices); |
512 | 154 | currentIndices = copyVariableIndices(); |
513 | 154 | pathCondition = pathCondition || clausesVisitResults[i].second; |
514 | 154 | } |
515 | 148 | setPathCondition(pathCondition); |
516 | | |
517 | 148 | return false; |
518 | 148 | } |
519 | | |
520 | | bool BMC::visit(Break const&) |
521 | 84 | { |
522 | 84 | LoopControl control = { |
523 | 84 | LoopControlKind::Break, |
524 | 84 | currentPathConditions(), |
525 | 84 | copyVariableIndices() |
526 | 84 | }; |
527 | 84 | m_loopCheckpoints.top().emplace_back(control); |
528 | 84 | return false; |
529 | 84 | } |
530 | | |
531 | | bool BMC::visit(Continue const&) |
532 | 69 | { |
533 | 69 | LoopControl control = { |
534 | 69 | LoopControlKind::Continue, |
535 | 69 | currentPathConditions(), |
536 | 69 | copyVariableIndices() |
537 | 69 | }; |
538 | 69 | m_loopCheckpoints.top().emplace_back(control); |
539 | 69 | return false; |
540 | 69 | } |
541 | | |
542 | | void BMC::endVisit(UnaryOperation const& _op) |
543 | 3.71k | { |
544 | 3.71k | SMTEncoder::endVisit(_op); |
545 | | |
546 | | // User-defined operators are essentially function calls. |
547 | 3.71k | if (auto funDef = *_op.annotation().userDefinedFunction) |
548 | 11 | { |
549 | 11 | std::vector<Expression const*> arguments; |
550 | 11 | arguments.push_back(&_op.subExpression()); |
551 | | // pushCallStack and defineExpr inside createReturnedExpression should be called on the expression |
552 | | // in case of a user-defined operator call |
553 | 11 | inlineFunctionCall(funDef, _op, std::nullopt, arguments); |
554 | 11 | return; |
555 | 11 | } |
556 | | |
557 | 3.70k | if ( |
558 | 3.70k | _op.annotation().type->category() == Type::Category::RationalNumber || |
559 | 2.97k | _op.annotation().type->category() == Type::Category::FixedPoint |
560 | 3.70k | ) |
561 | 774 | return; |
562 | | |
563 | 2.92k | if (_op.getOperator() == Token::Sub && smt::isInteger(*_op.annotation().type)) |
564 | 121 | { |
565 | 121 | addVerificationTarget( |
566 | 121 | VerificationTargetType::Underflow, |
567 | 121 | expr(_op), |
568 | 121 | &_op |
569 | 121 | ); |
570 | 121 | addVerificationTarget( |
571 | 121 | VerificationTargetType::Overflow, |
572 | 121 | expr(_op), |
573 | 121 | &_op |
574 | 121 | ); |
575 | 121 | } |
576 | 2.92k | } |
577 | | |
578 | | void BMC::endVisit(BinaryOperation const& _op) |
579 | 17.2k | { |
580 | 17.2k | SMTEncoder::endVisit(_op); |
581 | | |
582 | 17.2k | if (auto funDef = *_op.annotation().userDefinedFunction) |
583 | 30 | { |
584 | 30 | std::vector<Expression const*> arguments; |
585 | 30 | arguments.push_back(&_op.leftExpression()); |
586 | 30 | arguments.push_back(&_op.rightExpression()); |
587 | | |
588 | | // pushCallStack and defineExpr inside createReturnedExpression should be called on the expression |
589 | | // in case of a user-defined operator call |
590 | 30 | inlineFunctionCall(funDef, _op, std::nullopt, arguments); |
591 | 30 | } |
592 | 17.2k | } |
593 | | |
594 | | void BMC::endVisit(FunctionCall const& _funCall) |
595 | 13.8k | { |
596 | 13.8k | auto functionCallKind = *_funCall.annotation().kind; |
597 | | |
598 | 13.8k | if (functionCallKind != FunctionCallKind::FunctionCall) |
599 | 2.81k | { |
600 | 2.81k | SMTEncoder::endVisit(_funCall); |
601 | 2.81k | return; |
602 | 2.81k | } |
603 | | |
604 | 11.0k | FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type); |
605 | 11.0k | switch (funType.kind()) |
606 | 11.0k | { |
607 | 2.19k | case FunctionType::Kind::Assert: |
608 | 2.19k | visitAssert(_funCall); |
609 | 2.19k | SMTEncoder::endVisit(_funCall); |
610 | 2.19k | break; |
611 | 401 | case FunctionType::Kind::Require: |
612 | 401 | visitRequire(_funCall); |
613 | 401 | SMTEncoder::endVisit(_funCall); |
614 | 401 | break; |
615 | 1.89k | case FunctionType::Kind::Internal: |
616 | 2.74k | case FunctionType::Kind::External: |
617 | 2.92k | case FunctionType::Kind::DelegateCall: |
618 | 3.11k | case FunctionType::Kind::BareCall: |
619 | 3.11k | case FunctionType::Kind::BareCallCode: |
620 | 3.12k | case FunctionType::Kind::BareDelegateCall: |
621 | 3.13k | case FunctionType::Kind::BareStaticCall: |
622 | 3.43k | case FunctionType::Kind::Creation: |
623 | 3.43k | SMTEncoder::endVisit(_funCall); |
624 | 3.43k | internalOrExternalFunctionCall(_funCall); |
625 | 3.43k | break; |
626 | 16 | case FunctionType::Kind::Send: |
627 | 87 | case FunctionType::Kind::Transfer: |
628 | 87 | { |
629 | 87 | auto value = _funCall.arguments().front(); |
630 | 87 | solAssert(value, ""); |
631 | 87 | smtutil::Expression thisBalance = state().balance(); |
632 | | |
633 | 87 | addVerificationTarget( |
634 | 87 | VerificationTargetType::Balance, |
635 | 87 | thisBalance < expr(*value), |
636 | 87 | &_funCall |
637 | 87 | ); |
638 | | |
639 | 87 | SMTEncoder::endVisit(_funCall); |
640 | 87 | break; |
641 | 16 | } |
642 | 134 | case FunctionType::Kind::KECCAK256: |
643 | 154 | case FunctionType::Kind::ECRecover: |
644 | 169 | case FunctionType::Kind::SHA256: |
645 | 251 | case FunctionType::Kind::RIPEMD160: |
646 | 252 | case FunctionType::Kind::BlobHash: |
647 | 273 | case FunctionType::Kind::BlockHash: |
648 | 315 | case FunctionType::Kind::AddMod: |
649 | 328 | case FunctionType::Kind::MulMod: |
650 | 430 | case FunctionType::Kind::Unwrap: |
651 | 594 | case FunctionType::Kind::Wrap: |
652 | 594 | [[fallthrough]]; |
653 | 4.89k | default: |
654 | 4.89k | SMTEncoder::endVisit(_funCall); |
655 | 4.89k | break; |
656 | 11.0k | } |
657 | 11.0k | } |
658 | | |
659 | | void BMC::endVisit(Return const& _return) |
660 | 5.57k | { |
661 | 5.57k | SMTEncoder::endVisit(_return); |
662 | 5.57k | setPathCondition(smtutil::Expression(false)); |
663 | 5.57k | } |
664 | | |
665 | | /// Visitor helpers. |
666 | | |
667 | | void BMC::visitAssert(FunctionCall const& _funCall) |
668 | 2.19k | { |
669 | 2.19k | auto const& args = _funCall.arguments(); |
670 | 2.19k | solAssert(args.size() == 1, ""); |
671 | 2.19k | solAssert(args.front()->annotation().type->category() == Type::Category::Bool, ""); |
672 | 2.19k | addVerificationTarget( |
673 | 2.19k | VerificationTargetType::Assert, |
674 | 2.19k | expr(*args.front()), |
675 | 2.19k | &_funCall |
676 | 2.19k | ); |
677 | 2.19k | } |
678 | | |
679 | | void BMC::visitRequire(FunctionCall const& _funCall) |
680 | 401 | { |
681 | 401 | auto const& args = _funCall.arguments(); |
682 | 401 | solAssert(args.size() >= 1, ""); |
683 | 401 | solAssert(args.front()->annotation().type->category() == Type::Category::Bool, ""); |
684 | 401 | checkIfConditionIsConstant(*args.front()); |
685 | 401 | } |
686 | | |
687 | | void BMC::visitAddMulMod(FunctionCall const& _funCall) |
688 | 55 | { |
689 | 55 | solAssert(_funCall.arguments().at(2), ""); |
690 | 55 | addVerificationTarget( |
691 | 55 | VerificationTargetType::DivByZero, |
692 | 55 | expr(*_funCall.arguments().at(2)), |
693 | 55 | &_funCall |
694 | 55 | ); |
695 | | |
696 | 55 | SMTEncoder::visitAddMulMod(_funCall); |
697 | 55 | } |
698 | | |
699 | | void BMC::inlineFunctionCall( |
700 | | FunctionDefinition const* _funDef, |
701 | | Expression const& _callStackExpr, |
702 | | std::optional<Expression const*> _boundArgumentCall, |
703 | | std::vector<Expression const*> const& _arguments |
704 | | ) |
705 | 2.13k | { |
706 | 2.13k | solAssert(_funDef, ""); |
707 | | |
708 | 2.13k | if (visitedFunction(_funDef)) |
709 | 296 | { |
710 | 296 | auto const& returnParams = _funDef->returnParameters(); |
711 | 296 | for (auto param: returnParams) |
712 | 266 | { |
713 | 266 | m_context.newValue(*param); |
714 | 266 | m_context.setUnknownValue(*param); |
715 | 266 | } |
716 | 296 | } |
717 | 1.83k | else |
718 | 1.83k | { |
719 | 1.83k | initializeFunctionCallParameters(*_funDef, symbolicArguments(_funDef->parameters(), _arguments, _boundArgumentCall)); |
720 | | |
721 | | // The reason why we need to pushCallStack here instead of visit(FunctionDefinition) |
722 | | // is that there we don't have `_callStackExpr`. |
723 | 1.83k | pushCallStack({_funDef, &_callStackExpr}); |
724 | 1.83k | pushPathCondition(currentPathConditions()); |
725 | 1.83k | auto oldChecked = std::exchange(m_checked, true); |
726 | 1.83k | _funDef->accept(*this); |
727 | 1.83k | m_checked = oldChecked; |
728 | 1.83k | popPathCondition(); |
729 | 1.83k | } |
730 | | |
731 | 2.13k | createReturnedExpressions(_funDef, _callStackExpr); |
732 | 2.13k | } |
733 | | |
734 | | void BMC::inlineFunctionCall(FunctionCall const& _funCall) |
735 | 2.09k | { |
736 | 2.09k | solAssert(shouldInlineFunctionCall(_funCall, currentScopeContract(), m_currentContract), ""); |
737 | | |
738 | 2.09k | auto funDef = functionCallToDefinition(_funCall, currentScopeContract(), m_currentContract); |
739 | 2.09k | Expression const* expr = &_funCall.expression(); |
740 | 2.09k | auto funType = dynamic_cast<FunctionType const*>(expr->annotation().type); |
741 | 2.09k | std::optional<Expression const*> boundArgumentCall = |
742 | 2.09k | funType->hasBoundFirstArgument() ? std::make_optional(expr) : std::nullopt; |
743 | | |
744 | 2.09k | std::vector<Expression const*> arguments; |
745 | 2.09k | for (auto& arg: _funCall.sortedArguments()) |
746 | 985 | arguments.push_back(&(*arg)); |
747 | | |
748 | | // pushCallStack and defineExpr inside createReturnedExpression should be called |
749 | | // on the FunctionCall object for the normal function call case |
750 | 2.09k | inlineFunctionCall(funDef, _funCall, boundArgumentCall, arguments); |
751 | 2.09k | } |
752 | | |
753 | | void BMC::internalOrExternalFunctionCall(FunctionCall const& _funCall) |
754 | 3.43k | { |
755 | 3.43k | auto const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type); |
756 | 3.43k | if (shouldInlineFunctionCall(_funCall, currentScopeContract(), m_currentContract)) |
757 | 2.09k | inlineFunctionCall(_funCall); |
758 | 1.34k | else if (publicGetter(_funCall.expression())) |
759 | 183 | { |
760 | | // Do nothing here. |
761 | | // The processing happens in SMT Encoder, but we need to prevent the resetting of the state variables. |
762 | 183 | } |
763 | 1.16k | else if (funType.kind() == FunctionType::Kind::Internal) |
764 | 126 | m_unsupportedErrors.warning( |
765 | 126 | 5729_error, |
766 | 126 | _funCall.location(), |
767 | 126 | "BMC does not yet implement this type of function call." |
768 | 126 | ); |
769 | 1.03k | else if (funType.kind() == FunctionType::Kind::BareStaticCall) |
770 | 11 | { |
771 | | // Do nothing here. |
772 | | // Neither storage nor balances should be modified. |
773 | 11 | } |
774 | 1.02k | else |
775 | 1.02k | { |
776 | 1.02k | m_externalFunctionCallHappened = true; |
777 | 1.02k | resetStorageVariables(); |
778 | 1.02k | resetBalances(); |
779 | 1.02k | } |
780 | 3.43k | } |
781 | | |
782 | | std::pair<smtutil::Expression, smtutil::Expression> BMC::arithmeticOperation( |
783 | | Token _op, |
784 | | smtutil::Expression const& _left, |
785 | | smtutil::Expression const& _right, |
786 | | Type const* _commonType, |
787 | | Expression const& _expression |
788 | | ) |
789 | 8.79k | { |
790 | | // Unchecked does not disable div by 0 checks. |
791 | 8.79k | if (_op == Token::Div || _op == Token::Mod) |
792 | 766 | addVerificationTarget( |
793 | 766 | VerificationTargetType::DivByZero, |
794 | 766 | _right, |
795 | 766 | &_expression |
796 | 766 | ); |
797 | | |
798 | 8.79k | auto values = SMTEncoder::arithmeticOperation(_op, _left, _right, _commonType, _expression); |
799 | | |
800 | 8.79k | if (!m_checked) |
801 | 133 | return values; |
802 | | |
803 | 8.65k | auto const* intType = dynamic_cast<IntegerType const*>(_commonType); |
804 | 8.65k | if (!intType) |
805 | 170 | intType = TypeProvider::uint256(); |
806 | | |
807 | | // Mod does not need underflow/overflow checks. |
808 | 8.65k | if (_op == Token::Mod) |
809 | 351 | return values; |
810 | | |
811 | | // The order matters here: |
812 | | // If _op is Div and intType is signed, we only care about overflow. |
813 | 8.30k | if (_op == Token::Div) |
814 | 405 | { |
815 | 405 | if (intType->isSigned()) |
816 | | // Signed division can only overflow. |
817 | 31 | addVerificationTarget(VerificationTargetType::Overflow, values.second, &_expression); |
818 | 374 | else |
819 | | // Unsigned division cannot underflow/overflow. |
820 | 374 | return values; |
821 | 405 | } |
822 | 7.90k | else if (intType->isSigned()) |
823 | 384 | { |
824 | 384 | addVerificationTarget(VerificationTargetType::Overflow, values.second, &_expression); |
825 | 384 | addVerificationTarget(VerificationTargetType::Underflow, values.second, &_expression); |
826 | 384 | } |
827 | 7.51k | else if (_op == Token::Sub) |
828 | 3.16k | addVerificationTarget(VerificationTargetType::Underflow, values.second, &_expression); |
829 | 4.35k | else if (_op == Token::Add || _op == Token::Mul) |
830 | 4.35k | addVerificationTarget(VerificationTargetType::Overflow, values.second, &_expression); |
831 | 0 | else |
832 | 4.35k | solAssert(false, ""); |
833 | | |
834 | 7.93k | return values; |
835 | 8.30k | } |
836 | | |
837 | | void BMC::reset() |
838 | 14.5k | { |
839 | 14.5k | m_externalFunctionCallHappened = false; |
840 | 14.5k | m_loopExecutionHappened = false; |
841 | 14.5k | } |
842 | | |
843 | | std::pair<std::vector<smtutil::Expression>, std::vector<std::string>> BMC::modelExpressions() |
844 | 3.09k | { |
845 | 3.09k | std::vector<smtutil::Expression> expressionsToEvaluate; |
846 | 3.09k | std::vector<std::string> expressionNames; |
847 | 3.09k | for (auto const& var: m_context.variables()) |
848 | 7.20k | if (var.first->type()->isValueType()) |
849 | 5.24k | { |
850 | 5.24k | expressionsToEvaluate.emplace_back(currentValue(*var.first)); |
851 | 5.24k | expressionNames.push_back(var.first->name()); |
852 | 5.24k | } |
853 | 3.09k | for (auto const& var: m_context.globalSymbols()) |
854 | 297 | { |
855 | 297 | auto const& type = var.second->type(); |
856 | 297 | if ( |
857 | 297 | type->isValueType() && |
858 | 297 | smt::smtKind(*type) != smtutil::Kind::Function |
859 | 297 | ) |
860 | 20 | { |
861 | 20 | expressionsToEvaluate.emplace_back(var.second->currentValue()); |
862 | 20 | expressionNames.push_back(var.first); |
863 | 20 | } |
864 | 297 | } |
865 | 3.09k | for (auto const& uf: m_uninterpretedTerms) |
866 | 7.27k | if (uf->annotation().type->isValueType()) |
867 | 4.75k | { |
868 | 4.75k | expressionsToEvaluate.emplace_back(expr(*uf)); |
869 | 4.75k | std::string expressionName; |
870 | 4.75k | if (uf->location().hasText()) |
871 | 4.75k | expressionName = m_charStreamProvider.charStream(*uf->location().sourceName).text( |
872 | 4.75k | uf->location() |
873 | 4.75k | ); |
874 | 4.75k | expressionNames.push_back(std::move(expressionName)); |
875 | 4.75k | } |
876 | | |
877 | 3.09k | return {expressionsToEvaluate, expressionNames}; |
878 | 3.09k | } |
879 | | |
880 | | /// Verification targets. |
881 | | |
882 | | std::string BMC::targetDescription(BMCVerificationTarget const& _target) |
883 | 0 | { |
884 | 0 | if ( |
885 | 0 | _target.type == VerificationTargetType::Underflow || |
886 | 0 | _target.type == VerificationTargetType::Overflow |
887 | 0 | ) |
888 | 0 | { |
889 | 0 | auto const* intType = dynamic_cast<IntegerType const*>(_target.expression->annotation().type); |
890 | 0 | if (!intType) |
891 | 0 | intType = TypeProvider::uint256(); |
892 | |
|
893 | 0 | if (_target.type == VerificationTargetType::Underflow) |
894 | 0 | return "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ")"; |
895 | 0 | return "Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ")"; |
896 | 0 | } |
897 | 0 | else if (_target.type == VerificationTargetType::DivByZero) |
898 | 0 | return "Division by zero"; |
899 | 0 | else if (_target.type == VerificationTargetType::Assert) |
900 | 0 | return "Assertion violation"; |
901 | 0 | else if (_target.type == VerificationTargetType::Balance) |
902 | 0 | return "Insufficient funds"; |
903 | 0 | solAssert(false); |
904 | 0 | } |
905 | | |
906 | | void BMC::checkVerificationTargets() |
907 | 27.0k | { |
908 | 27.0k | for (auto& target: m_verificationTargets) |
909 | 3.07k | checkVerificationTarget(target); |
910 | 27.0k | } |
911 | | |
912 | | void BMC::checkVerificationTarget(BMCVerificationTarget& _target) |
913 | 3.07k | { |
914 | 3.07k | if ( |
915 | 3.07k | m_solvedTargets.count(_target.expression) && |
916 | 23 | m_solvedTargets.at(_target.expression).count(_target.type) |
917 | 3.07k | ) |
918 | 23 | return; |
919 | | |
920 | 3.04k | switch (_target.type) |
921 | 3.04k | { |
922 | 0 | case VerificationTargetType::Underflow: |
923 | 0 | checkUnderflow(_target); |
924 | 0 | break; |
925 | 0 | case VerificationTargetType::Overflow: |
926 | 0 | checkOverflow(_target); |
927 | 0 | break; |
928 | 798 | case VerificationTargetType::DivByZero: |
929 | 798 | checkDivByZero(_target); |
930 | 798 | break; |
931 | 86 | case VerificationTargetType::Balance: |
932 | 86 | checkBalance(_target); |
933 | 86 | break; |
934 | 2.16k | case VerificationTargetType::Assert: |
935 | 2.16k | checkAssert(_target); |
936 | 2.16k | break; |
937 | 0 | case VerificationTargetType::ConstantCondition: |
938 | 0 | smtAssert(false, "Checks for constant condition are handled separately"); |
939 | 0 | default: |
940 | 0 | smtAssert(false); |
941 | 3.04k | } |
942 | 3.04k | } |
943 | | |
944 | | void BMC::checkIfConditionIsConstant(Expression const& _condition) |
945 | 1.74k | { |
946 | 1.74k | if ( |
947 | 1.74k | !m_settings.targets.has(VerificationTargetType::ConstantCondition) || |
948 | 1.74k | (m_currentContract && !shouldEncode(*m_currentContract)) |
949 | 1.74k | ) |
950 | 7 | return; |
951 | | |
952 | | // We ignore called functions here because they have specific input values. |
953 | | // Also, expressions inside loop can have different values in different iterations. |
954 | 1.73k | if (!isRootFunction() || isInsideLoop()) |
955 | 391 | return; |
956 | | |
957 | | // Do not check for const-ness if this is a literal. |
958 | 1.34k | if (dynamic_cast<Literal const*>(&_condition)) |
959 | 73 | return; |
960 | | |
961 | 1.26k | auto [canBeTrue, canBeFalse] = checkBooleanNotConstant(currentPathConditions() && m_context.assertions(), expr(_condition)); |
962 | | |
963 | | // Report based on the result of the checks |
964 | 1.26k | if (canBeTrue == CheckResult::ERROR || canBeFalse == CheckResult::ERROR) |
965 | 0 | m_errorReporter.warning(8592_error, _condition.location(), "BMC: Error trying to invoke SMT solver."); |
966 | 1.26k | else if (canBeTrue == CheckResult::CONFLICTING || canBeFalse == CheckResult::CONFLICTING) |
967 | 0 | m_errorReporter.warning(3356_error, _condition.location(), "BMC: At least two SMT solvers provided conflicting answers. Results might not be sound."); |
968 | 1.26k | else if (canBeTrue == CheckResult::UNKNOWN || canBeFalse == CheckResult::UNKNOWN) |
969 | 1.26k | { |
970 | | // Not enough information to make definite claims. |
971 | 1.26k | } |
972 | 0 | else if (canBeTrue == CheckResult::SATISFIABLE && canBeFalse == CheckResult::SATISFIABLE) |
973 | 0 | { |
974 | | // Condition can be both true and false for some program runs. |
975 | 0 | } |
976 | | |
977 | 0 | else if (canBeTrue == CheckResult::UNSATISFIABLE && canBeFalse == CheckResult::UNSATISFIABLE) |
978 | 0 | m_errorReporter.warning(2512_error, _condition.location(), "BMC: Condition unreachable.", SMTEncoder::callStackMessage(m_callStack)); |
979 | 0 | else |
980 | 0 | { |
981 | 0 | std::string description; |
982 | 0 | if (canBeFalse == smtutil::CheckResult::UNSATISFIABLE) |
983 | 0 | { |
984 | 0 | smtAssert(canBeTrue == smtutil::CheckResult::SATISFIABLE); |
985 | 0 | description = "BMC: Condition is always true."; |
986 | 0 | } |
987 | 0 | else |
988 | 0 | { |
989 | 0 | smtAssert(canBeTrue == smtutil::CheckResult::UNSATISFIABLE); |
990 | 0 | smtAssert(canBeFalse == smtutil::CheckResult::SATISFIABLE); |
991 | 0 | description = "BMC: Condition is always false."; |
992 | 0 | } |
993 | 0 | m_errorReporter.warning( |
994 | 0 | 6838_error, |
995 | 0 | _condition.location(), |
996 | 0 | description, |
997 | 0 | SMTEncoder::callStackMessage(m_callStack) |
998 | 0 | ); |
999 | 0 | } |
1000 | | |
1001 | 1.26k | } |
1002 | | |
1003 | | void BMC::checkUnderflow(BMCVerificationTarget& _target) |
1004 | 0 | { |
1005 | 0 | solAssert( |
1006 | 0 | _target.type == VerificationTargetType::Underflow, |
1007 | 0 | "" |
1008 | 0 | ); |
1009 | |
|
1010 | 0 | auto const* intType = dynamic_cast<IntegerType const*>(_target.expression->annotation().type); |
1011 | 0 | if (!intType) |
1012 | 0 | intType = TypeProvider::uint256(); |
1013 | |
|
1014 | 0 | checkCondition( |
1015 | 0 | _target, |
1016 | 0 | _target.constraints && _target.value < smt::minValue(*intType), |
1017 | 0 | _target.callStack, |
1018 | 0 | _target.modelExpressions, |
1019 | 0 | _target.expression->location(), |
1020 | 0 | 4144_error, |
1021 | 0 | 8312_error, |
1022 | 0 | "<result>", |
1023 | 0 | &_target.value |
1024 | 0 | ); |
1025 | 0 | } |
1026 | | |
1027 | | void BMC::checkOverflow(BMCVerificationTarget& _target) |
1028 | 0 | { |
1029 | 0 | solAssert( |
1030 | 0 | _target.type == VerificationTargetType::Overflow, |
1031 | 0 | "" |
1032 | 0 | ); |
1033 | |
|
1034 | 0 | auto const* intType = dynamic_cast<IntegerType const*>(_target.expression->annotation().type); |
1035 | 0 | if (!intType) |
1036 | 0 | intType = TypeProvider::uint256(); |
1037 | |
|
1038 | 0 | checkCondition( |
1039 | 0 | _target, |
1040 | 0 | _target.constraints && _target.value > smt::maxValue(*intType), |
1041 | 0 | _target.callStack, |
1042 | 0 | _target.modelExpressions, |
1043 | 0 | _target.expression->location(), |
1044 | 0 | 2661_error, |
1045 | 0 | 8065_error, |
1046 | 0 | "<result>", |
1047 | 0 | &_target.value |
1048 | 0 | ); |
1049 | 0 | } |
1050 | | |
1051 | | void BMC::checkDivByZero(BMCVerificationTarget& _target) |
1052 | 798 | { |
1053 | 798 | solAssert(_target.type == VerificationTargetType::DivByZero, ""); |
1054 | | |
1055 | 798 | checkCondition( |
1056 | 798 | _target, |
1057 | 798 | _target.constraints && (_target.value == 0), |
1058 | 798 | _target.callStack, |
1059 | 798 | _target.modelExpressions, |
1060 | 798 | _target.expression->location(), |
1061 | 798 | 3046_error, |
1062 | 798 | 5272_error, |
1063 | 798 | "<result>", |
1064 | 798 | &_target.value |
1065 | 798 | ); |
1066 | 798 | } |
1067 | | |
1068 | | void BMC::checkBalance(BMCVerificationTarget& _target) |
1069 | 86 | { |
1070 | 86 | solAssert(_target.type == VerificationTargetType::Balance, ""); |
1071 | | |
1072 | 86 | checkCondition( |
1073 | 86 | _target, |
1074 | 86 | _target.constraints && _target.value, |
1075 | 86 | _target.callStack, |
1076 | 86 | _target.modelExpressions, |
1077 | 86 | _target.expression->location(), |
1078 | 86 | 1236_error, |
1079 | 86 | 4010_error, |
1080 | 86 | "address(this).balance" |
1081 | 86 | ); |
1082 | 86 | } |
1083 | | |
1084 | | void BMC::checkAssert(BMCVerificationTarget& _target) |
1085 | 2.16k | { |
1086 | 2.16k | solAssert(_target.type == VerificationTargetType::Assert, ""); |
1087 | | |
1088 | 2.16k | checkCondition( |
1089 | 2.16k | _target, |
1090 | 2.16k | _target.constraints && !_target.value, |
1091 | 2.16k | _target.callStack, |
1092 | 2.16k | _target.modelExpressions, |
1093 | 2.16k | _target.expression->location(), |
1094 | 2.16k | 4661_error, |
1095 | 2.16k | 7812_error |
1096 | 2.16k | ); |
1097 | 2.16k | } |
1098 | | |
1099 | | void BMC::addVerificationTarget( |
1100 | | VerificationTargetType _type, |
1101 | | smtutil::Expression const& _value, |
1102 | | Expression const* _expression |
1103 | | ) |
1104 | 11.6k | { |
1105 | 11.6k | smtAssert(_type != VerificationTargetType::ConstantCondition, "Checks for constant condition are handled separately"); |
1106 | 11.6k | if (!m_settings.targets.has(_type) || (m_currentContract && !shouldAnalyzeVerificationTargetsFor(*m_currentContract))) |
1107 | 8.56k | return; |
1108 | | |
1109 | 3.09k | BMCVerificationTarget target{ |
1110 | 3.09k | { |
1111 | 3.09k | _type, |
1112 | 3.09k | _value, |
1113 | 3.09k | currentPathConditions() && m_context.assertions() |
1114 | 3.09k | }, |
1115 | 3.09k | _expression, |
1116 | 3.09k | m_callStack, |
1117 | 3.09k | modelExpressions() |
1118 | 3.09k | }; |
1119 | 3.09k | m_verificationTargets.emplace_back(std::move(target)); |
1120 | 3.09k | } |
1121 | | |
1122 | | /// Solving. |
1123 | | |
1124 | | void BMC::checkCondition( |
1125 | | BMCVerificationTarget const& _target, |
1126 | | smtutil::Expression _condition, |
1127 | | std::vector<SMTEncoder::CallStackEntry> const& _callStack, |
1128 | | std::pair<std::vector<smtutil::Expression>, std::vector<std::string>> const& _modelExpressions, |
1129 | | SourceLocation const& _location, |
1130 | | ErrorId _errorHappens, |
1131 | | ErrorId _errorMightHappen, |
1132 | | std::string const& _additionalValueName, |
1133 | | smtutil::Expression const* _additionalValue |
1134 | | ) |
1135 | 3.04k | { |
1136 | 3.04k | m_interface->push(); |
1137 | 3.04k | m_interface->addAssertion(_condition); |
1138 | | |
1139 | 3.04k | std::vector<smtutil::Expression> expressionsToEvaluate; |
1140 | 3.04k | std::vector<std::string> expressionNames; |
1141 | 3.04k | tie(expressionsToEvaluate, expressionNames) = _modelExpressions; |
1142 | 3.04k | if (!_callStack.empty()) |
1143 | 3.04k | if (_additionalValue) |
1144 | 798 | { |
1145 | 798 | expressionsToEvaluate.emplace_back(*_additionalValue); |
1146 | 798 | expressionNames.push_back(_additionalValueName); |
1147 | 798 | } |
1148 | 3.04k | smtutil::CheckResult result; |
1149 | 3.04k | std::vector<std::string> values; |
1150 | 3.04k | tie(result, values) = checkSatisfiableAndGenerateModel(expressionsToEvaluate); |
1151 | | |
1152 | 3.04k | std::string extraComment = SMTEncoder::extraComment(); |
1153 | 3.04k | if (m_loopExecutionHappened) |
1154 | 118 | extraComment += |
1155 | 118 | "False negatives are possible when unrolling loops.\n" |
1156 | 118 | "This is due to the possibility that the BMC loop iteration setting is" |
1157 | 118 | " smaller than the actual number of iterations needed to complete a loop."; |
1158 | 3.04k | if (m_externalFunctionCallHappened) |
1159 | 76 | extraComment += |
1160 | 76 | "\nNote that external function calls are not inlined," |
1161 | 76 | " even if the source code of the function is available." |
1162 | 76 | " This is due to the possibility that the actual called contract" |
1163 | 76 | " has the same ABI but implements the function differently."; |
1164 | | |
1165 | 3.04k | SecondarySourceLocation secondaryLocation{}; |
1166 | 3.04k | secondaryLocation.append(extraComment, SourceLocation{}); |
1167 | | |
1168 | 3.04k | switch (result) |
1169 | 3.04k | { |
1170 | 0 | case smtutil::CheckResult::SATISFIABLE: |
1171 | 0 | { |
1172 | 0 | solAssert(!_callStack.empty(), ""); |
1173 | 0 | std::ostringstream message; |
1174 | 0 | message << "BMC: " << targetDescription(_target) << " happens here."; |
1175 | |
|
1176 | 0 | std::ostringstream modelMessage; |
1177 | | // Sometimes models have complex smtlib2 expressions that SMTLib2Interface fails to parse. |
1178 | 0 | if (values.size() == expressionNames.size()) |
1179 | 0 | { |
1180 | 0 | modelMessage << "Counterexample:\n"; |
1181 | 0 | std::map<std::string, std::string> sortedModel; |
1182 | 0 | for (size_t i = 0; i < values.size(); ++i) |
1183 | 0 | if (expressionsToEvaluate.at(i).name != values.at(i)) |
1184 | 0 | sortedModel[expressionNames.at(i)] = values.at(i); |
1185 | |
|
1186 | 0 | for (auto const& eval: sortedModel) |
1187 | 0 | modelMessage << " " << eval.first << " = " << eval.second << "\n"; |
1188 | 0 | } |
1189 | |
|
1190 | 0 | m_errorReporter.warning( |
1191 | 0 | _errorHappens, |
1192 | 0 | _location, |
1193 | 0 | message.str(), |
1194 | 0 | SecondarySourceLocation().append(modelMessage.str(), SourceLocation{}) |
1195 | 0 | .append(SMTEncoder::callStackMessage(_callStack)) |
1196 | 0 | .append(std::move(secondaryLocation)) |
1197 | 0 | ); |
1198 | 0 | break; |
1199 | 0 | } |
1200 | 0 | case smtutil::CheckResult::UNSATISFIABLE: |
1201 | 0 | { |
1202 | 0 | m_safeTargets[_target.expression].insert(_target); |
1203 | 0 | break; |
1204 | 0 | } |
1205 | 3.04k | case smtutil::CheckResult::UNKNOWN: |
1206 | 3.04k | { |
1207 | 3.04k | ++m_unprovedAmt; |
1208 | 3.04k | if (m_settings.showUnproved) |
1209 | 0 | m_errorReporter.warning(_errorMightHappen, _location, "BMC: " + targetDescription(_target) + " might happen here.", secondaryLocation); |
1210 | 3.04k | break; |
1211 | 0 | } |
1212 | 0 | case smtutil::CheckResult::CONFLICTING: |
1213 | 0 | m_errorReporter.warning(1584_error, _location, "BMC: At least two SMT solvers provided conflicting answers. Results might not be sound."); |
1214 | 0 | break; |
1215 | 0 | case smtutil::CheckResult::ERROR: |
1216 | 0 | m_errorReporter.warning(1823_error, _location, "BMC: Error during interaction with the SMT solver."); |
1217 | 0 | break; |
1218 | 3.04k | } |
1219 | | |
1220 | 3.04k | m_interface->pop(); |
1221 | 3.04k | } |
1222 | | |
1223 | | BMC::ConstantExpressionCheckResult BMC::checkBooleanNotConstant( |
1224 | | smtutil::Expression const& _constraints, |
1225 | | smtutil::Expression const& _condition |
1226 | | ) |
1227 | 1.26k | { |
1228 | 1.26k | m_interface->push(); |
1229 | 1.26k | m_interface->addAssertion(_constraints && _condition); |
1230 | 1.26k | auto positiveResult = checkSatisfiable(); |
1231 | 1.26k | m_interface->pop(); |
1232 | | |
1233 | 1.26k | m_interface->push(); |
1234 | 1.26k | m_interface->addAssertion(_constraints && !_condition); |
1235 | 1.26k | auto negatedResult = checkSatisfiable(); |
1236 | 1.26k | m_interface->pop(); |
1237 | | |
1238 | 1.26k | return {.canBeTrue = positiveResult, .canBeFalse = negatedResult}; |
1239 | 1.26k | } |
1240 | | |
1241 | | std::pair<smtutil::CheckResult, std::vector<std::string>> |
1242 | | BMC::checkSatisfiableAndGenerateModel(std::vector<smtutil::Expression> const& _expressionsToEvaluate) |
1243 | 5.58k | { |
1244 | 5.58k | smtutil::CheckResult result; |
1245 | 5.58k | std::vector<std::string> values; |
1246 | 5.58k | try |
1247 | 5.58k | { |
1248 | 5.58k | if (m_settings.printQuery) |
1249 | 0 | { |
1250 | 0 | auto portfolio = dynamic_cast<smtutil::SMTPortfolio*>(m_interface.get()); |
1251 | 0 | std::string smtlibCode = portfolio->dumpQuery(_expressionsToEvaluate); |
1252 | 0 | m_errorReporter.info( |
1253 | 0 | 6240_error, |
1254 | 0 | "BMC: Requested query:\n" + smtlibCode |
1255 | 0 | ); |
1256 | 0 | result = CheckResult::UNKNOWN; |
1257 | 0 | } |
1258 | 5.58k | else |
1259 | 5.58k | tie(result, values) = m_interface->check(_expressionsToEvaluate); |
1260 | 5.58k | } |
1261 | 5.58k | catch (smtutil::SolverError const& _e) |
1262 | 5.58k | { |
1263 | 0 | std::string description("BMC: Error querying SMT solver"); |
1264 | 0 | if (_e.comment()) |
1265 | 0 | description += ": " + *_e.comment(); |
1266 | 0 | m_errorReporter.warning(8140_error, description); |
1267 | 0 | result = smtutil::CheckResult::ERROR; |
1268 | 0 | } |
1269 | | |
1270 | 5.58k | for (std::string& value: values) |
1271 | 0 | { |
1272 | 0 | try |
1273 | 0 | { |
1274 | | // Parse and re-format nicely |
1275 | 0 | value = formatNumberReadable(bigint(value)); |
1276 | 0 | } |
1277 | 0 | catch (...) { } |
1278 | 0 | } |
1279 | | |
1280 | 5.58k | return make_pair(result, values); |
1281 | 5.58k | } |
1282 | | |
1283 | | smtutil::CheckResult BMC::checkSatisfiable() |
1284 | 2.53k | { |
1285 | 2.53k | return checkSatisfiableAndGenerateModel({}).first; |
1286 | 2.53k | } |
1287 | | |
1288 | | void BMC::assignment(smt::SymbolicVariable& _symVar, smtutil::Expression const& _value) |
1289 | 24.4k | { |
1290 | 24.4k | auto oldVar = _symVar.currentValue(); |
1291 | 24.4k | auto newVar = _symVar.increaseIndex(); |
1292 | 24.4k | m_context.addAssertion(smtutil::Expression::ite( |
1293 | 24.4k | currentPathConditions(), |
1294 | 24.4k | newVar == _value, |
1295 | 24.4k | newVar == oldVar |
1296 | 24.4k | )); |
1297 | 24.4k | } |
1298 | | |
1299 | | bool BMC::isInsideLoop() const |
1300 | 1.52k | { |
1301 | 1.52k | return !m_loopCheckpoints.empty(); |
1302 | 1.52k | } |