Coverage Report

Created: 2026-06-30 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}