Coverage Report

Created: 2026-07-13 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/libsolidity/formal/CHC.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/CHC.h>
20
21
#include <libsolidity/formal/ArraySlicePredicate.h>
22
#include <libsolidity/formal/ExpressionFormatter.h>
23
#include <libsolidity/formal/EldaricaCHCSmtLib2Interface.h>
24
#include <libsolidity/formal/ModelChecker.h>
25
#include <libsolidity/formal/PredicateInstance.h>
26
#include <libsolidity/formal/PredicateSort.h>
27
#include <libsolidity/formal/SymbolicTypes.h>
28
#include <libsolidity/formal/Z3CHCSmtLib2Interface.h>
29
30
#include <libsolidity/ast/TypeProvider.h>
31
32
#include <libsmtutil/CHCSmtLib2Interface.h>
33
#include <liblangutil/CharStreamProvider.h>
34
#include <libsolutil/Algorithms.h>
35
#include <libsolutil/StringUtils.h>
36
37
#include <range/v3/algorithm/for_each.hpp>
38
#include <range/v3/algorithm/none_of.hpp>
39
#include <range/v3/view.hpp>
40
#include <range/v3/view/enumerate.hpp>
41
#include <range/v3/view/reverse.hpp>
42
43
#include <charconv>
44
#include <queue>
45
46
using namespace solidity;
47
using namespace solidity::util;
48
using namespace solidity::langutil;
49
using namespace solidity::smtutil;
50
using namespace solidity::frontend;
51
using namespace solidity::frontend::smt;
52
53
CHC::CHC(
54
  EncodingContext& _context,
55
  UniqueErrorReporter& _errorReporter,
56
  UniqueErrorReporter& _unsupportedErrorReporter,
57
  ErrorReporter& _provedSafeReporter,
58
  std::map<util::h256, std::string> const& _smtlib2Responses,
59
  ReadCallback::Callback const& _smtCallback,
60
  ModelCheckerSettings _settings,
61
  CharStreamProvider const& _charStreamProvider
62
):
63
14.6k
  SMTEncoder(_context, _settings, _errorReporter, _unsupportedErrorReporter, _provedSafeReporter, _charStreamProvider),
64
14.6k
  m_smtlib2Responses(_smtlib2Responses),
65
14.6k
  m_smtCallback(_smtCallback)
66
14.6k
{}
67
68
void CHC::analyze(SourceUnit const& _source)
69
15.5k
{
70
  // At this point every enabled solver is available.
71
15.5k
  if (!m_settings.solvers.eld && !m_settings.solvers.smtlib2 && !m_settings.solvers.z3)
72
0
  {
73
0
    m_errorReporter.warning(
74
0
      7649_error,
75
0
      SourceLocation(),
76
0
      "CHC analysis was not possible since no Horn solver was found and enabled."
77
0
      " The accepted solvers for CHC are Eldarica and z3."
78
0
    );
79
0
    return;
80
0
  }
81
82
15.5k
  if (m_settings.solvers.eld && m_settings.solvers.z3)
83
0
    m_errorReporter.warning(
84
0
      5798_error,
85
0
      SourceLocation(),
86
0
      "Multiple Horn solvers were selected for CHC."
87
0
      " CHC only supports one solver at a time, therefore only z3 will be used."
88
0
      " If you wish to use Eldarica, please enable Eldarica only."
89
0
    );
90
91
15.5k
  if (!shouldAnalyzeVerificationTargetsFor(_source))
92
0
    return;
93
94
15.5k
  resetSourceAnalysis();
95
96
15.5k
  auto sources = sourceDependencies(_source);
97
15.5k
  collectFreeFunctions(sources);
98
15.5k
  createFreeConstants(sources);
99
15.5k
  state().prepareForSourceUnit(_source, encodeExternalCallsAsTrusted());
100
101
15.5k
  for (auto const* source: sources)
102
16.1k
    defineInterfacesAndSummaries(*source);
103
15.5k
  for (auto const* source: sources)
104
16.1k
    source->accept(*this);
105
106
15.5k
  checkVerificationTargets();
107
108
15.5k
  bool ranSolver = true;
109
  // If ranSolver is true here it's because an SMT solver callback was
110
  // actually given and the queries were solved,
111
  // or Eldarica was chosen and was present in the system.
112
15.5k
  if (auto const* smtLibInterface = dynamic_cast<CHCSmtLib2Interface const*>(m_interface.get()))
113
15.5k
    ranSolver = smtLibInterface->unhandledQueries().empty();
114
15.5k
  if (!ranSolver)
115
3.74k
    m_errorReporter.warning(
116
3.74k
      3996_error,
117
3.74k
      SourceLocation(),
118
3.74k
      "CHC analysis was not possible. No Horn solver was available."
119
3.74k
      " None of the installed solvers was enabled."
120
3.74k
    );
121
15.5k
}
122
123
std::vector<std::string> CHC::unhandledQueries() const
124
14.6k
{
125
14.6k
  if (auto smtlib2 = dynamic_cast<CHCSmtLib2Interface const*>(m_interface.get()))
126
13.9k
    return smtlib2->unhandledQueries();
127
128
661
  return {};
129
14.6k
}
130
131
bool CHC::visit(ContractDefinition const& _contract)
132
16.8k
{
133
16.8k
  if (!shouldEncode(_contract))
134
534
    return false;
135
136
  // Raises UnimplementedFeatureError in the presence of transient storage variables
137
16.2k
  TransientDataLocationChecker checker(_contract);
138
139
16.2k
  resetContractAnalysis();
140
16.2k
  initContract(_contract);
141
16.2k
  clearIndices(&_contract);
142
143
16.2k
  m_scopes.push_back(&_contract);
144
145
16.2k
  m_stateVariables = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract);
146
16.2k
  solAssert(m_currentContract, "");
147
148
16.2k
  SMTEncoder::visit(_contract);
149
16.2k
  return false;
150
16.8k
}
151
152
void CHC::endVisit(ContractDefinition const& _contract)
153
16.8k
{
154
16.8k
  if (!shouldEncode(_contract))
155
534
    return;
156
157
16.2k
  for (auto base: _contract.annotation().linearizedBaseContracts)
158
18.0k
  {
159
18.0k
    if (auto constructor = base->constructor())
160
2.29k
      constructor->accept(*this);
161
18.0k
    defineContractInitializer(*base, _contract);
162
18.0k
  }
163
164
16.2k
  auto const& entry = *createConstructorBlock(_contract, "implicit_constructor_entry");
165
166
  // In case constructors use uninitialized state variables,
167
  // they need to be zeroed.
168
  // This is not part of `initialConstraints` because it's only true here,
169
  // at the beginning of the deployment routine.
170
16.2k
  smtutil::Expression zeroes(true);
171
16.2k
  for (auto var: stateVariablesIncludingInheritedAndPrivate(_contract))
172
9.05k
    zeroes = zeroes && currentValue(*var) == smt::zeroValue(var->type());
173
174
16.2k
  smtutil::Expression newAddress = encodeExternalCallsAsTrusted() ?
175
0
    !state().addressActive(state().thisAddress()) :
176
16.2k
    smtutil::Expression(true);
177
178
  // The contract's address might already have funds before deployment,
179
  // so the balance must be at least `msg.value`, but not equals.
180
16.2k
  auto initialBalanceConstraint = state().balance(state().thisAddress()) >= state().txMember("msg.value");
181
16.2k
  addRule(smtutil::Expression::implies(
182
16.2k
    initialConstraints(_contract) && zeroes && newAddress && initialBalanceConstraint,
183
16.2k
    predicate(entry)
184
16.2k
  ), entry.functor().name);
185
186
16.2k
  setCurrentBlock(entry);
187
188
16.2k
  if (encodeExternalCallsAsTrusted())
189
0
  {
190
0
    auto const& entryAfterAddress = *createConstructorBlock(_contract, "implicit_constructor_entry_after_address");
191
0
    state().setAddressActive(state().thisAddress(), true);
192
193
0
    connectBlocks(m_currentBlock, predicate(entryAfterAddress));
194
0
    setCurrentBlock(entryAfterAddress);
195
0
  }
196
197
16.2k
  solAssert(!m_errorDest, "");
198
16.2k
  m_errorDest = m_constructorSummaries.at(&_contract);
199
  // We need to evaluate the base constructor calls (arguments) from derived -> base
200
16.2k
  auto baseArgs = baseArguments(_contract);
201
16.2k
  for (auto base: _contract.annotation().linearizedBaseContracts)
202
18.0k
    if (base != &_contract)
203
1.76k
    {
204
1.76k
      m_callGraph[&_contract].insert(base);
205
206
1.76k
      auto baseConstructor = base->constructor();
207
1.76k
      if (baseConstructor && baseArgs.count(base))
208
332
      {
209
332
        std::vector<ASTPointer<Expression>> const& args = baseArgs.at(base);
210
332
        auto const& params = baseConstructor->parameters();
211
332
        solAssert(params.size() == args.size(), "");
212
689
        for (unsigned i = 0; i < params.size(); ++i)
213
357
        {
214
357
          args.at(i)->accept(*this);
215
357
          if (params.at(i))
216
357
          {
217
357
            solAssert(m_context.knownVariable(*params.at(i)), "");
218
357
            m_context.addAssertion(currentValue(*params.at(i)) == expr(*args.at(i), params.at(i)->type()));
219
357
          }
220
357
        }
221
332
      }
222
1.76k
    }
223
16.2k
  m_errorDest = nullptr;
224
  // Then call initializer_Base from base -> derived
225
16.2k
  for (auto base: _contract.annotation().linearizedBaseContracts | ranges::views::reverse)
226
18.0k
  {
227
18.0k
    errorFlag().increaseIndex();
228
18.0k
    m_context.addAssertion(smt::constructorCall(*m_contractInitializers.at(&_contract).at(base), m_context));
229
18.0k
    connectBlocks(m_currentBlock, summary(_contract), errorFlag().currentValue() > 0);
230
18.0k
    m_context.addAssertion(errorFlag().currentValue() == 0);
231
18.0k
  }
232
233
16.2k
  if (encodeExternalCallsAsTrusted())
234
0
    state().writeStateVars(_contract, state().thisAddress());
235
236
16.2k
  connectBlocks(m_currentBlock, summary(_contract));
237
238
16.2k
  setCurrentBlock(*m_constructorSummaries.at(&_contract));
239
240
16.2k
  solAssert(&_contract == m_currentContract, "");
241
16.2k
  smtAssert(shouldEncode(_contract));
242
16.2k
  auto constructor = _contract.constructor();
243
16.2k
  auto txConstraints = state().txTypeConstraints();
244
16.2k
  if (!constructor || !constructor->isPayable())
245
16.1k
    txConstraints = txConstraints && state().txNonPayableConstraint();
246
16.2k
  connectBlocks(m_currentBlock, interface(), txConstraints && errorFlag().currentValue() == 0);
247
16.2k
  if (shouldAnalyzeVerificationTargetsFor(_contract))
248
16.2k
    m_queryPlaceholders[&_contract].push_back({txConstraints, errorFlag().currentValue(), m_currentBlock});
249
250
16.2k
  solAssert(m_scopes.back() == &_contract, "");
251
16.2k
  m_scopes.pop_back();
252
253
16.2k
  SMTEncoder::endVisit(_contract);
254
16.2k
}
255
256
bool CHC::visit(FunctionDefinition const& _function)
257
18.6k
{
258
  // Free functions need to be visited in the context of a contract.
259
18.6k
  if (!m_currentContract)
260
1.20k
    return false;
261
262
17.4k
  if (
263
17.4k
    !_function.isImplemented() ||
264
17.2k
    abstractAsNondet(_function)
265
17.4k
  )
266
190
  {
267
190
    smtutil::Expression conj(true);
268
190
    if (
269
190
      _function.stateMutability() == StateMutability::Pure ||
270
158
      _function.stateMutability() == StateMutability::View
271
190
    )
272
84
      conj = conj && currentEqualInitialVarsConstraints(stateVariablesIncludingInheritedAndPrivate(_function));
273
274
190
    conj = conj && errorFlag().currentValue() == 0;
275
190
    addRule(smtutil::Expression::implies(conj, summary(_function)), "summary_function_" + std::to_string(_function.id()));
276
190
    return false;
277
190
  }
278
279
  // No inlining.
280
17.2k
  solAssert(!m_currentFunction, "Function inlining should not happen in CHC.");
281
17.2k
  m_currentFunction = &_function;
282
283
17.2k
  m_scopes.push_back(&_function);
284
285
17.2k
  initFunction(_function);
286
287
17.2k
  auto functionEntryBlock = createBlock(m_currentFunction, PredicateType::FunctionBlock);
288
17.2k
  auto bodyBlock = createBlock(&m_currentFunction->body(), PredicateType::FunctionBlock);
289
290
17.2k
  auto functionPred = predicate(*functionEntryBlock);
291
17.2k
  auto bodyPred = predicate(*bodyBlock);
292
293
17.2k
  addRule(functionPred, functionPred.name);
294
295
17.2k
  solAssert(m_currentContract, "");
296
17.2k
  m_context.addAssertion(initialConstraints(*m_currentContract, &_function));
297
298
17.2k
  connectBlocks(functionPred, bodyPred);
299
300
17.2k
  setCurrentBlock(*bodyBlock);
301
302
17.2k
  solAssert(!m_errorDest, "");
303
17.2k
  m_errorDest = m_summaries.at(m_currentContract).at(&_function);
304
17.2k
  SMTEncoder::visit(*m_currentFunction);
305
17.2k
  m_errorDest = nullptr;
306
307
17.2k
  return false;
308
17.4k
}
309
310
void CHC::endVisit(FunctionDefinition const& _function)
311
18.6k
{
312
  // Free functions need to be visited in the context of a contract.
313
18.6k
  if (!m_currentContract)
314
1.20k
    return;
315
316
17.4k
  if (
317
17.4k
    !_function.isImplemented() ||
318
17.2k
    abstractAsNondet(_function)
319
17.4k
  )
320
190
    return;
321
322
17.2k
  solAssert(m_currentFunction && m_currentContract, "");
323
  // No inlining.
324
17.2k
  solAssert(m_currentFunction == &_function, "");
325
326
17.2k
  solAssert(m_scopes.back() == &_function, "");
327
17.2k
  m_scopes.pop_back();
328
329
17.2k
  connectBlocks(m_currentBlock, summary(_function));
330
17.2k
  setCurrentBlock(*m_summaries.at(m_currentContract).at(&_function));
331
332
  // Query placeholders for constructors are not created here because
333
  // of contracts without constructors.
334
  // Instead, those are created in endVisit(ContractDefinition).
335
17.2k
  if (
336
17.2k
    !_function.isConstructor() &&
337
14.9k
    _function.isPublic() &&
338
13.1k
    contractFunctions(*m_currentContract).count(&_function) &&
339
12.7k
    shouldEncode(*m_currentContract)
340
17.2k
  )
341
12.7k
  {
342
12.7k
    defineExternalFunctionInterface(_function, *m_currentContract);
343
12.7k
    setCurrentBlock(*m_interfaces.at(m_currentContract));
344
345
    // Create the rule
346
    // interface \land externalFunctionEntry => interface'
347
12.7k
    auto ifacePre = smt::interfacePre(*m_interfaces.at(m_currentContract), *m_currentContract, m_context);
348
12.7k
    auto sum = externalSummary(_function);
349
350
12.7k
    connectBlocks(ifacePre, interface(), sum && errorFlag().currentValue() == 0);
351
12.7k
    if (shouldAnalyzeVerificationTargetsFor(*m_currentContract))
352
12.7k
      m_queryPlaceholders[&_function].push_back({sum, errorFlag().currentValue(), ifacePre});
353
12.7k
  }
354
355
17.2k
  m_currentFunction = nullptr;
356
357
17.2k
  SMTEncoder::endVisit(_function);
358
17.2k
}
359
360
bool CHC::visit(Block const& _block)
361
19.0k
{
362
19.0k
  m_scopes.push_back(&_block);
363
19.0k
  return SMTEncoder::visit(_block);
364
19.0k
}
365
366
void CHC::endVisit(Block const& _block)
367
19.0k
{
368
19.0k
  solAssert(m_scopes.back() == &_block, "");
369
19.0k
  m_scopes.pop_back();
370
19.0k
  SMTEncoder::endVisit(_block);
371
19.0k
}
372
373
bool CHC::visit(IfStatement const& _if)
374
726
{
375
726
  solAssert(m_currentFunction, "");
376
377
726
  bool unknownFunctionCallWasSeen = m_unknownFunctionCallSeen;
378
726
  m_unknownFunctionCallSeen = false;
379
380
726
  solAssert(m_currentFunction, "");
381
726
  auto const& functionBody = m_currentFunction->body();
382
383
726
  auto ifHeaderBlock = createBlock(&_if, PredicateType::FunctionBlock, "if_header_");
384
726
  auto trueBlock = createBlock(&_if.trueStatement(), PredicateType::FunctionBlock, "if_true_");
385
726
  auto falseBlock = _if.falseStatement() ? createBlock(_if.falseStatement(), PredicateType::FunctionBlock, "if_false_") : nullptr;
386
726
  auto afterIfBlock = createBlock(&functionBody, PredicateType::FunctionBlock);
387
388
726
  connectBlocks(m_currentBlock, predicate(*ifHeaderBlock));
389
390
726
  setCurrentBlock(*ifHeaderBlock);
391
726
  _if.condition().accept(*this);
392
726
  auto condition = expr(_if.condition());
393
394
726
  connectBlocks(m_currentBlock, predicate(*trueBlock), condition);
395
726
  if (_if.falseStatement())
396
98
    connectBlocks(m_currentBlock, predicate(*falseBlock), !condition);
397
628
  else
398
628
    connectBlocks(m_currentBlock, predicate(*afterIfBlock), !condition);
399
400
726
  setCurrentBlock(*trueBlock);
401
726
  _if.trueStatement().accept(*this);
402
726
  connectBlocks(m_currentBlock, predicate(*afterIfBlock));
403
404
726
  if (_if.falseStatement())
405
98
  {
406
98
    setCurrentBlock(*falseBlock);
407
98
    _if.falseStatement()->accept(*this);
408
98
    connectBlocks(m_currentBlock, predicate(*afterIfBlock));
409
98
  }
410
411
726
  setCurrentBlock(*afterIfBlock);
412
413
726
  if (m_unknownFunctionCallSeen)
414
5
    eraseKnowledge();
415
416
726
  m_unknownFunctionCallSeen = unknownFunctionCallWasSeen;
417
418
726
  return false;
419
726
}
420
421
bool CHC::visit(WhileStatement const& _while)
422
196
{
423
196
  bool unknownFunctionCallWasSeen = m_unknownFunctionCallSeen;
424
196
  m_unknownFunctionCallSeen = false;
425
426
196
  solAssert(m_currentFunction, "");
427
196
  auto const& functionBody = m_currentFunction->body();
428
429
196
  auto namePrefix = std::string(_while.isDoWhile() ? "do_" : "") + "while";
430
196
  auto loopHeaderBlock = createBlock(&_while, PredicateType::FunctionBlock, namePrefix + "_header_");
431
196
  auto loopBodyBlock = createBlock(&_while.body(), PredicateType::FunctionBlock, namePrefix + "_body_");
432
196
  auto afterLoopBlock = createBlock(&functionBody, PredicateType::FunctionBlock);
433
434
196
  auto outerBreakDest = m_breakDest;
435
196
  auto outerContinueDest = m_continueDest;
436
196
  m_breakDest = afterLoopBlock;
437
196
  m_continueDest = loopHeaderBlock;
438
439
196
  if (_while.isDoWhile())
440
34
    _while.body().accept(*this);
441
442
196
  connectBlocks(m_currentBlock, predicate(*loopHeaderBlock));
443
444
196
  setCurrentBlock(*loopHeaderBlock);
445
446
196
  _while.condition().accept(*this);
447
196
  auto condition = expr(_while.condition());
448
449
196
  connectBlocks(m_currentBlock, predicate(*loopBodyBlock), condition);
450
196
  connectBlocks(m_currentBlock, predicate(*afterLoopBlock), !condition);
451
452
  // Loop body visit.
453
196
  setCurrentBlock(*loopBodyBlock);
454
196
  _while.body().accept(*this);
455
456
196
  m_breakDest = outerBreakDest;
457
196
  m_continueDest = outerContinueDest;
458
459
  // Back edge.
460
196
  connectBlocks(m_currentBlock, predicate(*loopHeaderBlock));
461
196
  setCurrentBlock(*afterLoopBlock);
462
463
196
  if (m_unknownFunctionCallSeen)
464
0
    eraseKnowledge();
465
466
196
  m_unknownFunctionCallSeen = unknownFunctionCallWasSeen;
467
468
196
  return false;
469
196
}
470
471
bool CHC::visit(ForStatement const& _for)
472
695
{
473
695
  m_scopes.push_back(&_for);
474
475
695
  bool unknownFunctionCallWasSeen = m_unknownFunctionCallSeen;
476
695
  m_unknownFunctionCallSeen = false;
477
478
695
  solAssert(m_currentFunction, "");
479
695
  auto const& functionBody = m_currentFunction->body();
480
481
695
  auto loopHeaderBlock = createBlock(&_for, PredicateType::FunctionBlock, "for_header_");
482
695
  auto loopBodyBlock = createBlock(&_for.body(), PredicateType::FunctionBlock, "for_body_");
483
695
  auto afterLoopBlock = createBlock(&functionBody, PredicateType::FunctionBlock);
484
695
  auto postLoop = _for.loopExpression();
485
695
  auto postLoopBlock = postLoop ? createBlock(postLoop, PredicateType::FunctionBlock, "for_post_") : nullptr;
486
487
695
  auto outerBreakDest = m_breakDest;
488
695
  auto outerContinueDest = m_continueDest;
489
695
  m_breakDest = afterLoopBlock;
490
695
  m_continueDest = postLoop ? postLoopBlock : loopHeaderBlock;
491
492
695
  if (auto init = _for.initializationExpression())
493
628
    init->accept(*this);
494
495
695
  connectBlocks(m_currentBlock, predicate(*loopHeaderBlock));
496
695
  setCurrentBlock(*loopHeaderBlock);
497
498
695
  auto condition = smtutil::Expression(true);
499
695
  if (auto forCondition = _for.condition())
500
657
  {
501
657
    forCondition->accept(*this);
502
657
    condition = expr(*forCondition);
503
657
  }
504
505
695
  connectBlocks(m_currentBlock, predicate(*loopBodyBlock), condition);
506
695
  connectBlocks(m_currentBlock, predicate(*afterLoopBlock), !condition);
507
508
  // Loop body visit.
509
695
  setCurrentBlock(*loopBodyBlock);
510
695
  _for.body().accept(*this);
511
512
695
  if (postLoop)
513
629
  {
514
629
    connectBlocks(m_currentBlock, predicate(*postLoopBlock));
515
629
    setCurrentBlock(*postLoopBlock);
516
629
    postLoop->accept(*this);
517
629
  }
518
519
695
  m_breakDest = outerBreakDest;
520
695
  m_continueDest = outerContinueDest;
521
522
  // Back edge.
523
695
  connectBlocks(m_currentBlock, predicate(*loopHeaderBlock));
524
695
  setCurrentBlock(*afterLoopBlock);
525
526
695
  if (m_unknownFunctionCallSeen)
527
0
    eraseKnowledge();
528
529
695
  m_unknownFunctionCallSeen = unknownFunctionCallWasSeen;
530
531
695
  return false;
532
695
}
533
534
void CHC::endVisit(ForStatement const& _for)
535
695
{
536
695
  solAssert(m_scopes.back() == &_for, "");
537
695
  m_scopes.pop_back();
538
695
}
539
540
void CHC::endVisit(UnaryOperation const& _op)
541
4.18k
{
542
4.18k
  SMTEncoder::endVisit(_op);
543
544
4.18k
  if (auto funDef = *_op.annotation().userDefinedFunction)
545
12
  {
546
12
    std::vector<Expression const*> arguments;
547
12
    arguments.push_back(&_op.subExpression());
548
12
    internalFunctionCall(funDef, std::nullopt, _op.userDefinedFunctionType(), arguments, state().thisAddress());
549
550
12
    createReturnedExpressions(funDef, _op);
551
12
    return;
552
12
  }
553
554
4.17k
  if (
555
4.17k
    _op.annotation().type->category() == Type::Category::RationalNumber ||
556
3.16k
    _op.annotation().type->category() == Type::Category::FixedPoint
557
4.17k
  )
558
1.07k
    return;
559
560
3.10k
  if (_op.getOperator() == Token::Sub && smt::isInteger(*_op.annotation().type))
561
131
  {
562
131
    auto const* intType = dynamic_cast<IntegerType const*>(_op.annotation().type);
563
131
    if (!intType)
564
0
      intType = TypeProvider::uint256();
565
566
131
    verificationTargetEncountered(&_op, VerificationTargetType::Underflow, expr(_op) < intType->minValue());
567
131
    verificationTargetEncountered(&_op, VerificationTargetType::Overflow, expr(_op) > intType->maxValue());
568
131
  }
569
3.10k
}
570
571
void CHC::endVisit(BinaryOperation const& _op)
572
18.2k
{
573
18.2k
  SMTEncoder::endVisit(_op);
574
575
18.2k
  if (auto funDef = *_op.annotation().userDefinedFunction)
576
30
  {
577
30
    std::vector<Expression const*> arguments;
578
30
    arguments.push_back(&_op.leftExpression());
579
30
    arguments.push_back(&_op.rightExpression());
580
30
    internalFunctionCall(funDef, std::nullopt, _op.userDefinedFunctionType(), arguments, state().thisAddress());
581
582
30
    createReturnedExpressions(funDef, _op);
583
30
  }
584
18.2k
}
585
586
void CHC::endVisit(FunctionCall const& _funCall)
587
14.7k
{
588
14.7k
  auto functionCallKind = *_funCall.annotation().kind;
589
590
14.7k
  if (functionCallKind != FunctionCallKind::FunctionCall)
591
3.02k
  {
592
3.02k
    SMTEncoder::endVisit(_funCall);
593
3.02k
    return;
594
3.02k
  }
595
596
11.6k
  FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
597
11.6k
  switch (funType.kind())
598
11.6k
  {
599
2.47k
  case FunctionType::Kind::Assert:
600
2.47k
    visitAssert(_funCall);
601
2.47k
    SMTEncoder::endVisit(_funCall);
602
2.47k
    break;
603
1.80k
  case FunctionType::Kind::Internal:
604
1.80k
    internalFunctionCall(_funCall);
605
1.80k
    break;
606
938
  case FunctionType::Kind::External:
607
949
  case FunctionType::Kind::BareStaticCall:
608
1.25k
  case FunctionType::Kind::BareCall:
609
1.25k
    externalFunctionCall(_funCall);
610
1.25k
    SMTEncoder::endVisit(_funCall);
611
1.25k
    break;
612
329
  case FunctionType::Kind::Creation:
613
329
    visitDeployment(_funCall);
614
329
    break;
615
180
  case FunctionType::Kind::DelegateCall:
616
180
  case FunctionType::Kind::BareCallCode:
617
192
  case FunctionType::Kind::BareDelegateCall:
618
192
    SMTEncoder::endVisit(_funCall);
619
192
    unknownFunctionCall(_funCall);
620
192
    break;
621
15
  case FunctionType::Kind::Send:
622
84
  case FunctionType::Kind::Transfer:
623
84
  {
624
84
    auto value = _funCall.arguments().front();
625
84
    solAssert(value, "");
626
84
    smtutil::Expression thisBalance = state().balance();
627
628
84
    verificationTargetEncountered(
629
84
      &_funCall,
630
84
      VerificationTargetType::Balance,
631
84
      thisBalance < expr(*value)
632
84
    );
633
634
84
    SMTEncoder::endVisit(_funCall);
635
84
    break;
636
15
  }
637
137
  case FunctionType::Kind::KECCAK256:
638
155
  case FunctionType::Kind::ECRecover:
639
168
  case FunctionType::Kind::SHA256:
640
258
  case FunctionType::Kind::RIPEMD160:
641
259
  case FunctionType::Kind::BlobHash:
642
284
  case FunctionType::Kind::BlockHash:
643
325
  case FunctionType::Kind::AddMod:
644
339
  case FunctionType::Kind::MulMod:
645
429
  case FunctionType::Kind::Unwrap:
646
592
  case FunctionType::Kind::Wrap:
647
592
    [[fallthrough]];
648
5.55k
  default:
649
5.55k
    SMTEncoder::endVisit(_funCall);
650
5.55k
    break;
651
11.6k
  }
652
653
11.6k
  auto funDef = functionCallToDefinition(_funCall, currentScopeContract(), m_currentContract);
654
11.6k
  createReturnedExpressions(funDef, _funCall);
655
11.6k
}
656
657
void CHC::endVisit(Break const& _break)
658
99
{
659
99
  solAssert(m_breakDest, "");
660
99
  connectBlocks(m_currentBlock, predicate(*m_breakDest));
661
662
  // Add an unreachable ghost node to collect unreachable statements after a break.
663
99
  auto breakGhost = createBlock(&_break, PredicateType::FunctionBlock, "break_ghost_");
664
99
  m_currentBlock = predicate(*breakGhost);
665
99
}
666
667
void CHC::endVisit(Continue const& _continue)
668
85
{
669
85
  solAssert(m_continueDest, "");
670
85
  connectBlocks(m_currentBlock, predicate(*m_continueDest));
671
672
  // Add an unreachable ghost node to collect unreachable statements after a continue.
673
85
  auto continueGhost = createBlock(&_continue, PredicateType::FunctionBlock, "continue_ghost_");
674
85
  m_currentBlock = predicate(*continueGhost);
675
85
}
676
677
void CHC::endVisit(IndexRangeAccess const& _range)
678
579
{
679
579
  createExpr(_range);
680
681
579
  auto baseArray = std::dynamic_pointer_cast<SymbolicArrayVariable>(m_context.expression(_range.baseExpression()));
682
579
  auto sliceArray = std::dynamic_pointer_cast<SymbolicArrayVariable>(m_context.expression(_range));
683
579
  solAssert(baseArray && sliceArray, "");
684
685
579
  auto const& sliceData = ArraySlicePredicate::create(sliceArray->sort(), m_context);
686
579
  if (!sliceData.first)
687
121
  {
688
121
    for (auto pred: sliceData.second.predicates)
689
363
      m_interface->registerRelation(pred->functor());
690
121
    for (auto const& rule: sliceData.second.rules)
691
484
      addRule(rule, "");
692
121
  }
693
694
579
  auto start = _range.startExpression() ? expr(*_range.startExpression()) : 0;
695
579
  auto end = _range.endExpression() ? expr(*_range.endExpression()) : baseArray->length();
696
579
  auto slicePred = (*sliceData.second.predicates.at(0))({
697
579
    baseArray->elements(),
698
579
    sliceArray->elements(),
699
579
    start,
700
579
    end
701
579
  });
702
703
579
  m_context.addAssertion(slicePred);
704
579
  m_context.addAssertion(sliceArray->length() == end - start);
705
579
}
706
707
void CHC::endVisit(Return const& _return)
708
5.72k
{
709
5.72k
  SMTEncoder::endVisit(_return);
710
711
5.72k
  connectBlocks(m_currentBlock, predicate(*m_returnDests.back()));
712
713
  // Add an unreachable ghost node to collect unreachable statements after a return.
714
5.72k
  auto returnGhost = createBlock(&_return, PredicateType::FunctionBlock, "return_ghost_");
715
5.72k
  m_currentBlock = predicate(*returnGhost);
716
5.72k
}
717
718
bool CHC::visit(TryCatchClause const& _tryStatement)
719
298
{
720
298
  m_scopes.push_back(&_tryStatement);
721
298
  return SMTEncoder::visit(_tryStatement);
722
298
}
723
724
void CHC::endVisit(TryCatchClause const& _tryStatement)
725
298
{
726
298
  solAssert(m_scopes.back() == &_tryStatement, "");
727
298
  m_scopes.pop_back();
728
298
}
729
730
bool CHC::visit(TryStatement const& _tryStatement)
731
144
{
732
144
  FunctionCall const* externalCall = dynamic_cast<FunctionCall const*>(&_tryStatement.externalCall());
733
144
  solAssert(externalCall && externalCall->annotation().tryCall, "");
734
144
  solAssert(m_currentFunction, "");
735
736
144
  auto tryHeaderBlock = createBlock(&_tryStatement, PredicateType::FunctionBlock, "try_header_");
737
144
  auto afterTryBlock = createBlock(&m_currentFunction->body(), PredicateType::FunctionBlock);
738
739
144
  auto const& clauses = _tryStatement.clauses();
740
144
  solAssert(clauses[0].get() == _tryStatement.successClause(), "First clause of TryStatement should be the success clause");
741
298
  auto clauseBlocks = applyMap(clauses, [this](ASTPointer<TryCatchClause> clause) {
742
298
    return createBlock(clause.get(), PredicateType::FunctionBlock, "try_clause_" + std::to_string(clause->id()));
743
298
  });
744
745
144
  connectBlocks(m_currentBlock, predicate(*tryHeaderBlock));
746
144
  setCurrentBlock(*tryHeaderBlock);
747
  // Visit everything, except the actual external call.
748
144
  externalCall->expression().accept(*this);
749
144
  ASTNode::listAccept(externalCall->arguments(), *this);
750
  // Branch directly to all catch clauses, since in these cases, any effects of the external call are reverted.
751
298
  for (size_t i = 1; i < clauseBlocks.size(); ++i)
752
154
    connectBlocks(m_currentBlock, predicate(*clauseBlocks[i]));
753
  // Only now visit the actual call to record its effects and connect to the success clause.
754
144
  endVisit(*externalCall);
755
144
  if (_tryStatement.successClause()->parameters())
756
36
    expressionToTupleAssignment(_tryStatement.successClause()->parameters()->parameters(), *externalCall);
757
758
144
  connectBlocks(m_currentBlock, predicate(*clauseBlocks[0]));
759
760
442
  for (size_t i = 0; i < clauses.size(); ++i)
761
298
  {
762
298
    setCurrentBlock(*clauseBlocks[i]);
763
298
    clauses[i]->accept(*this);
764
298
    connectBlocks(m_currentBlock, predicate(*afterTryBlock));
765
298
  }
766
144
  setCurrentBlock(*afterTryBlock);
767
768
144
  return false;
769
144
}
770
771
void CHC::pushInlineFrame(CallableDeclaration const& _callable)
772
17.8k
{
773
17.8k
  m_returnDests.push_back(createBlock(&_callable, PredicateType::FunctionBlock, "return_"));
774
17.8k
}
775
776
void CHC::popInlineFrame(CallableDeclaration const& _callable)
777
17.8k
{
778
17.8k
  solAssert(!m_returnDests.empty(), "");
779
17.8k
  auto const& ret = *m_returnDests.back();
780
17.8k
  solAssert(ret.programNode() == &_callable, "");
781
17.8k
  connectBlocks(m_currentBlock, predicate(ret));
782
17.8k
  setCurrentBlock(ret);
783
17.8k
  m_returnDests.pop_back();
784
17.8k
}
785
786
void CHC::visitAssert(FunctionCall const& _funCall)
787
2.47k
{
788
2.47k
  auto const& args = _funCall.arguments();
789
2.47k
  solAssert(args.size() == 1, "");
790
2.47k
  solAssert(args.front()->annotation().type->category() == Type::Category::Bool, "");
791
792
2.47k
  solAssert(m_currentContract, "");
793
2.47k
  solAssert(m_currentFunction, "");
794
2.47k
  auto errorCondition = !m_context.expression(*args.front())->currentValue();
795
2.47k
  verificationTargetEncountered(&_funCall, VerificationTargetType::Assert, errorCondition);
796
2.47k
}
797
798
void CHC::visitPublicGetter(FunctionCall const& _funCall)
799
312
{
800
312
  createExpr(_funCall);
801
312
  if (encodeExternalCallsAsTrusted())
802
0
  {
803
0
    auto const& access = dynamic_cast<MemberAccess const&>(_funCall.expression());
804
0
    auto const& contractType = dynamic_cast<ContractType const&>(*access.expression().annotation().type);
805
0
    state().writeStateVars(*m_currentContract, state().thisAddress());
806
0
    state().readStateVars(contractType.contractDefinition(), expr(access.expression()));
807
0
  }
808
312
  SMTEncoder::visitPublicGetter(_funCall);
809
312
}
810
811
void CHC::visitAddMulMod(FunctionCall const& _funCall)
812
55
{
813
55
  solAssert(_funCall.arguments().at(2), "");
814
815
55
  verificationTargetEncountered(&_funCall, VerificationTargetType::DivByZero, expr(*_funCall.arguments().at(2)) == 0);
816
817
55
  SMTEncoder::visitAddMulMod(_funCall);
818
55
}
819
820
void CHC::visitDeployment(FunctionCall const& _funCall)
821
329
{
822
329
  if (!encodeExternalCallsAsTrusted())
823
329
  {
824
329
    SMTEncoder::endVisit(_funCall);
825
329
    unknownFunctionCall(_funCall);
826
329
    return;
827
329
  }
828
829
0
  auto [callExpr, callOptions] = functionCallExpression(_funCall);
830
0
  auto funType = dynamic_cast<FunctionType const*>(callExpr->annotation().type);
831
0
  ContractDefinition const* contract =
832
0
    &dynamic_cast<ContractType const&>(*funType->returnParameterTypes().front()).contractDefinition();
833
834
  // copy state variables from m_currentContract to state.storage.
835
0
  state().writeStateVars(*m_currentContract, state().thisAddress());
836
0
  errorFlag().increaseIndex();
837
838
0
  Expression const* value = valueOption(callOptions);
839
0
  if (value)
840
0
    decreaseBalanceFromOptionsValue(*value);
841
842
0
  auto originalTx = state().tx();
843
0
  newTxConstraints(value);
844
845
0
  auto prevThisAddr = state().thisAddress();
846
0
  auto newAddr = state().newThisAddress();
847
848
0
  if (auto constructor = contract->constructor())
849
0
  {
850
0
    auto const& args = _funCall.sortedArguments();
851
0
    auto const& params = constructor->parameters();
852
0
    solAssert(args.size() == params.size(), "");
853
0
    for (auto [arg, param]: ranges::zip_view(args, params))
854
0
      m_context.addAssertion(expr(*arg, param->type()) == m_context.variable(*param)->currentValue());
855
0
  }
856
0
  for (auto var: stateVariablesIncludingInheritedAndPrivate(*contract))
857
0
    m_context.variable(*var)->increaseIndex();
858
0
  Predicate const& constructorSummary = *m_constructorSummaries.at(contract);
859
0
  m_context.addAssertion(smt::constructorCall(constructorSummary, m_context, false));
860
861
0
  solAssert(m_errorDest, "");
862
0
  connectBlocks(
863
0
    m_currentBlock,
864
0
    predicate(*m_errorDest),
865
0
    errorFlag().currentValue() > 0
866
0
  );
867
0
  m_context.addAssertion(errorFlag().currentValue() == 0);
868
869
0
  m_context.addAssertion(state().newThisAddress() == prevThisAddr);
870
871
  // copy state variables from state.storage to m_currentContract.
872
0
  state().readStateVars(*m_currentContract, state().thisAddress());
873
874
0
  state().newTx();
875
0
  m_context.addAssertion(originalTx == state().tx());
876
877
0
  defineExpr(_funCall, newAddr);
878
0
}
879
880
void CHC::internalFunctionCall(
881
  FunctionDefinition const* _funDef,
882
  std::optional<Expression const*> _boundArgumentCall,
883
  FunctionType const* _funType,
884
  std::vector<Expression const*> const& _arguments,
885
  smtutil::Expression _contractAddressValue
886
)
887
1.84k
{
888
1.84k
  solAssert(m_currentContract, "");
889
1.84k
  solAssert(_funType, "");
890
891
1.84k
  if (_funDef)
892
1.73k
  {
893
1.73k
    if (m_currentFunction && !m_currentFunction->isConstructor())
894
1.45k
      m_callGraph[m_currentFunction].insert(_funDef);
895
288
    else
896
288
      m_callGraph[m_currentContract].insert(_funDef);
897
1.73k
  }
898
899
1.84k
  m_context.addAssertion(predicate(_funDef, _boundArgumentCall, _funType, _arguments, _contractAddressValue));
900
901
1.84k
  solAssert(m_errorDest, "");
902
1.84k
  connectBlocks(
903
1.84k
    m_currentBlock,
904
1.84k
    predicate(*m_errorDest),
905
1.84k
    errorFlag().currentValue() > 0 && currentPathConditions()
906
1.84k
  );
907
1.84k
  m_context.addAssertion(smtutil::Expression::implies(currentPathConditions(), errorFlag().currentValue() == 0));
908
1.84k
  m_context.addAssertion(errorFlag().increaseIndex() == 0);
909
1.84k
}
910
911
void CHC::internalFunctionCall(FunctionCall const& _funCall)
912
1.80k
{
913
1.80k
  solAssert(m_currentContract, "");
914
915
1.80k
  auto funDef = functionCallToDefinition(_funCall, currentScopeContract(), m_currentContract);
916
1.80k
  if (funDef)
917
1.69k
  {
918
1.69k
    if (m_currentFunction && !m_currentFunction->isConstructor())
919
1.40k
      m_callGraph[m_currentFunction].insert(funDef);
920
288
    else
921
288
      m_callGraph[m_currentContract].insert(funDef);
922
1.69k
  }
923
924
1.80k
  Expression const* calledExpr = &_funCall.expression();
925
1.80k
  auto funType = dynamic_cast<FunctionType const*>(calledExpr->annotation().type);
926
927
1.80k
  std::vector<Expression const*> arguments;
928
1.80k
  for (auto& arg: _funCall.sortedArguments())
929
786
    arguments.push_back(&(*arg));
930
931
1.80k
  std::optional<Expression const*> boundArgumentCall =
932
1.80k
    funType->hasBoundFirstArgument() ? std::make_optional(calledExpr) : std::nullopt;
933
1.80k
  internalFunctionCall(funDef, boundArgumentCall, funType, arguments, contractAddressValue(_funCall));
934
1.80k
}
935
936
void CHC::addNondetCalls(ContractDefinition const& _contract)
937
0
{
938
0
  for (auto var: _contract.stateVariables())
939
0
    if (auto contractType = dynamic_cast<ContractType const*>(var->type()))
940
0
    {
941
0
      auto const& symbVar = m_context.variable(*var);
942
0
      m_context.addAssertion(symbVar->currentValue() == symbVar->valueAtIndex(0));
943
0
      nondetCall(contractType->contractDefinition(), *var);
944
0
    }
945
0
}
946
947
void CHC::nondetCall(ContractDefinition const& _contract, VariableDeclaration const& _var)
948
0
{
949
0
  auto address = m_context.variable(_var)->currentValue();
950
  // Load the called contract's state variables from the global state.
951
0
  state().readStateVars(_contract, address);
952
953
0
  m_context.addAssertion(state().state() == state().state(0));
954
0
  auto preCallState = std::vector<smtutil::Expression>{state().state()} + currentStateVariables(_contract);
955
956
0
  state().newState();
957
0
  for (auto const* var: _contract.stateVariables())
958
0
    m_context.variable(*var)->increaseIndex();
959
960
0
  Predicate const& callPredicate = *createSymbolicBlock(
961
0
    nondetInterfaceSort(_contract, state()),
962
0
    "nondet_call_" + uniquePrefix(),
963
0
    PredicateType::FunctionSummary,
964
0
    &_var,
965
0
    m_currentContract
966
0
  );
967
0
  auto postCallState = std::vector<smtutil::Expression>{state().state()} + currentStateVariables(_contract);
968
0
  std::vector<smtutil::Expression> stateExprs = commonStateExpressions(errorFlag().increaseIndex(), address);
969
970
0
  auto nondet = (*m_nondetInterfaces.at(&_contract))(stateExprs + preCallState + postCallState);
971
0
  auto nondetCall = callPredicate(stateExprs + preCallState + postCallState);
972
973
0
  addRule(smtutil::Expression::implies(nondet, nondetCall), nondetCall.name);
974
975
0
  m_context.addAssertion(nondetCall);
976
977
  // Load the called contract's state variables into the global state.
978
0
  state().writeStateVars(_contract, address);
979
0
}
980
981
void CHC::externalFunctionCall(FunctionCall const& _funCall)
982
1.25k
{
983
  /// In external function calls we do not add a "predicate call"
984
  /// because we do not trust their function body anyway,
985
  /// so we just add the nondet_interface predicate.
986
987
1.25k
  solAssert(m_currentContract, "");
988
989
1.25k
  auto [callExpr, callOptions] = functionCallExpression(_funCall);
990
1.25k
  FunctionType const& funType = dynamic_cast<FunctionType const&>(*callExpr->annotation().type);
991
992
1.25k
  auto kind = funType.kind();
993
1.25k
  solAssert(
994
1.25k
    kind == FunctionType::Kind::External ||
995
1.25k
    kind == FunctionType::Kind::BareCall ||
996
1.25k
    kind == FunctionType::Kind::BareStaticCall,
997
1.25k
    ""
998
1.25k
  );
999
1000
1001
  // Only consider high level external calls in trusted mode.
1002
1.25k
  if (
1003
1.25k
    kind == FunctionType::Kind::External &&
1004
938
    (encodeExternalCallsAsTrusted() || isExternalCallToThis(callExpr))
1005
1.25k
  )
1006
532
  {
1007
532
    externalFunctionCallToTrustedCode(_funCall);
1008
532
    return;
1009
532
  }
1010
1011
  // Low level calls are still encoded nondeterministically.
1012
1013
719
  auto function = functionCallToDefinition(_funCall, currentScopeContract(), m_currentContract);
1014
719
  if (function)
1015
275
    for (auto var: function->returnParameters())
1016
174
      m_context.variable(*var)->increaseIndex();
1017
1018
  // If we see a low level call in trusted mode,
1019
  // we need to havoc the global state.
1020
719
  if (
1021
719
    kind == FunctionType::Kind::BareCall &&
1022
302
    encodeExternalCallsAsTrusted()
1023
719
  )
1024
0
    state().newStorage();
1025
1026
  // No reentrancy from constructor calls.
1027
719
  if (!m_currentFunction || m_currentFunction->isConstructor())
1028
227
    return;
1029
1030
492
  if (Expression const* value = valueOption(callOptions))
1031
27
    decreaseBalanceFromOptionsValue(*value);
1032
1033
492
  auto preCallState = std::vector<smtutil::Expression>{state().state()} + currentStateVariables();
1034
1035
492
  if (!usesStaticCall(_funCall))
1036
428
  {
1037
428
    state().newState();
1038
428
    for (auto const* var: m_stateVariables)
1039
155
      m_context.variable(*var)->increaseIndex();
1040
428
  }
1041
1042
492
  Predicate const& callPredicate = *createSymbolicBlock(
1043
492
    nondetInterfaceSort(*m_currentContract, state()),
1044
492
    "nondet_call_" + uniquePrefix(),
1045
492
    PredicateType::ExternalCallUntrusted,
1046
492
    &_funCall
1047
492
  );
1048
492
  auto postCallState = std::vector<smtutil::Expression>{state().state()} + currentStateVariables();
1049
492
  std::vector<smtutil::Expression> stateExprs = commonStateExpressions(errorFlag().increaseIndex(), state().thisAddress());
1050
1051
492
  auto nondet = (*m_nondetInterfaces.at(m_currentContract))(stateExprs + preCallState + postCallState);
1052
492
  auto nondetCall = callPredicate(stateExprs + preCallState + postCallState);
1053
1054
492
  addRule(smtutil::Expression::implies(nondet, nondetCall), nondetCall.name);
1055
1056
492
  m_context.addAssertion(nondetCall);
1057
492
  solAssert(m_errorDest, "");
1058
492
  connectBlocks(m_currentBlock, predicate(*m_errorDest), errorFlag().currentValue() > 0 && currentPathConditions());
1059
1060
  // To capture the possibility of a reentrant call, we record in the call graph that the  current function
1061
  // can call any of the external methods of the current contract.
1062
492
  if (m_currentFunction)
1063
492
    for (auto const* definedFunction: contractFunctions(*m_currentContract))
1064
809
      if (!definedFunction->isConstructor() && definedFunction->isPublic())
1065
717
        m_callGraph[m_currentFunction].insert(definedFunction);
1066
1067
492
  m_context.addAssertion(errorFlag().currentValue() == 0);
1068
492
}
1069
1070
void CHC::externalFunctionCallToTrustedCode(FunctionCall const& _funCall)
1071
532
{
1072
532
  if (publicGetter(_funCall.expression()))
1073
117
    visitPublicGetter(_funCall);
1074
1075
532
  solAssert(m_currentContract, "");
1076
1077
532
  auto [callExpr, callOptions] = functionCallExpression(_funCall);
1078
532
  FunctionType const& funType = dynamic_cast<FunctionType const&>(*callExpr->annotation().type);
1079
1080
532
  auto kind = funType.kind();
1081
532
  solAssert(kind == FunctionType::Kind::External || kind == FunctionType::Kind::BareStaticCall, "");
1082
1083
532
  auto function = functionCallToDefinition(_funCall, currentScopeContract(), m_currentContract);
1084
532
  if (!function)
1085
117
    return;
1086
1087
  // Remember the external call in the call graph to properly detect verification targets for the current function
1088
415
  if (m_currentFunction && !m_currentFunction->isConstructor())
1089
402
    m_callGraph[m_currentFunction].insert(function);
1090
13
  else
1091
13
    m_callGraph[m_currentContract].insert(function);
1092
1093
  // External call creates a new transaction.
1094
415
  auto originalTx = state().tx();
1095
415
  Expression const* value = valueOption(callOptions);
1096
415
  newTxConstraints(value);
1097
1098
415
  auto calledAddress = contractAddressValue(_funCall);
1099
415
  if (value)
1100
7
  {
1101
7
    decreaseBalanceFromOptionsValue(*value);
1102
7
    state().addBalance(calledAddress, expr(*value));
1103
7
  }
1104
1105
415
  if (encodeExternalCallsAsTrusted())
1106
0
  {
1107
    // The order here is important!! Write should go first.
1108
1109
    // Load the caller contract's state variables into the global state.
1110
0
    state().writeStateVars(*m_currentContract, state().thisAddress());
1111
    // Load the called contract's state variables from the global state.
1112
0
    state().readStateVars(*function->annotation().contract, contractAddressValue(_funCall));
1113
0
  }
1114
1115
415
  std::vector<Expression const*> arguments;
1116
415
  for (auto& arg: _funCall.sortedArguments())
1117
193
    arguments.push_back(&(*arg));
1118
415
  smtutil::Expression pred = predicate(function, std::nullopt, &funType, arguments, calledAddress);
1119
1120
415
  auto txConstraints = state().txTypeConstraints() && state().txFunctionConstraints(*function);
1121
415
  m_context.addAssertion(pred && txConstraints);
1122
  // restore the original transaction data
1123
415
  state().newTx();
1124
415
  m_context.addAssertion(originalTx == state().tx());
1125
1126
415
  solAssert(m_errorDest, "");
1127
415
  connectBlocks(
1128
415
    m_currentBlock,
1129
415
    predicate(*m_errorDest),
1130
415
    (errorFlag().currentValue() > 0)
1131
415
  );
1132
415
  m_context.addAssertion(errorFlag().currentValue() == 0);
1133
1134
415
  if (!usesStaticCall(_funCall))
1135
367
    if (encodeExternalCallsAsTrusted())
1136
0
    {
1137
      // The order here is important!! Write should go first.
1138
1139
      // Load the called contract's state variables into the global state.
1140
0
      state().writeStateVars(*function->annotation().contract, contractAddressValue(_funCall));
1141
      // Load the caller contract's state variables from the global state.
1142
0
      state().readStateVars(*m_currentContract, state().thisAddress());
1143
0
    }
1144
415
}
1145
1146
void CHC::unknownFunctionCall(FunctionCall const&)
1147
521
{
1148
  /// Function calls are not handled at the moment,
1149
  /// so always erase knowledge.
1150
  /// TODO remove when function calls get predicates/blocks.
1151
521
  eraseKnowledge();
1152
1153
  /// Used to erase outer scope knowledge in loops and ifs.
1154
  /// TODO remove when function calls get predicates/blocks.
1155
521
  m_unknownFunctionCallSeen = true;
1156
521
}
1157
1158
void CHC::makeArrayPopVerificationTarget(FunctionCall const& _arrayPop)
1159
218
{
1160
218
  FunctionType const& funType = dynamic_cast<FunctionType const&>(*_arrayPop.expression().annotation().type);
1161
218
  solAssert(funType.kind() == FunctionType::Kind::ArrayPop, "");
1162
1163
218
  auto memberAccess = dynamic_cast<MemberAccess const*>(cleanExpression(_arrayPop.expression()));
1164
218
  solAssert(memberAccess, "");
1165
218
  auto symbArray = std::dynamic_pointer_cast<SymbolicArrayVariable>(m_context.expression(memberAccess->expression()));
1166
218
  solAssert(symbArray, "");
1167
1168
218
  verificationTargetEncountered(&_arrayPop, VerificationTargetType::PopEmptyArray, symbArray->length() <= 0);
1169
218
}
1170
1171
void CHC::makeOutOfBoundsVerificationTarget(IndexAccess const& _indexAccess)
1172
16.1k
{
1173
16.1k
  if (_indexAccess.annotation().type->category() == Type::Category::TypeType)
1174
0
    return;
1175
1176
16.1k
  auto baseType = _indexAccess.baseExpression().annotation().type;
1177
1178
16.1k
  std::optional<smtutil::Expression> length;
1179
16.1k
  if (smt::isArray(*baseType))
1180
14.3k
    length = dynamic_cast<smt::SymbolicArrayVariable const&>(
1181
14.3k
      *m_context.expression(_indexAccess.baseExpression())
1182
14.3k
    ).length();
1183
1.78k
  else if (auto const* type = dynamic_cast<FixedBytesType const*>(baseType))
1184
534
    length = smtutil::Expression(static_cast<size_t>(type->numBytes()));
1185
1186
16.1k
  std::optional<smtutil::Expression> target;
1187
16.1k
  if (
1188
16.1k
    auto index = _indexAccess.indexExpression();
1189
16.1k
    index && length
1190
16.1k
  )
1191
14.8k
    target = expr(*index) < 0 || expr(*index) >= *length;
1192
1193
16.1k
  if (target)
1194
14.8k
    verificationTargetEncountered(&_indexAccess, VerificationTargetType::OutOfBounds, *target);
1195
16.1k
}
1196
1197
std::pair<smtutil::Expression, smtutil::Expression> CHC::arithmeticOperation(
1198
  Token _op,
1199
  smtutil::Expression const& _left,
1200
  smtutil::Expression const& _right,
1201
  Type const* _commonType,
1202
  frontend::Expression const& _expression
1203
)
1204
10.1k
{
1205
  // Unchecked does not disable div by 0 checks.
1206
10.1k
  if (_op == Token::Mod || _op == Token::Div)
1207
861
    verificationTargetEncountered(&_expression, VerificationTargetType::DivByZero, _right == 0);
1208
1209
10.1k
  auto values = SMTEncoder::arithmeticOperation(_op, _left, _right, _commonType, _expression);
1210
1211
10.1k
  if (!m_checked)
1212
114
    return values;
1213
1214
10.0k
  IntegerType const* intType = nullptr;
1215
10.0k
  if (auto const* type = dynamic_cast<IntegerType const*>(_commonType))
1216
9.75k
    intType = type;
1217
305
  else
1218
305
    intType = TypeProvider::uint256();
1219
1220
  // Mod does not need underflow/overflow checks.
1221
  // Div only needs overflow check for signed types.
1222
10.0k
  if (_op == Token::Mod || (_op == Token::Div && !intType->isSigned()))
1223
819
    return values;
1224
1225
9.24k
  if (_op == Token::Div)
1226
32
    verificationTargetEncountered(&_expression, VerificationTargetType::Overflow, values.second > intType->maxValue());
1227
9.20k
  else if (intType->isSigned())
1228
419
  {
1229
419
    verificationTargetEncountered(&_expression, VerificationTargetType::Underflow, values.second < intType->minValue());
1230
419
    verificationTargetEncountered(&_expression, VerificationTargetType::Overflow, values.second > intType->maxValue());
1231
419
  }
1232
8.79k
  else if (_op == Token::Sub)
1233
3.80k
    verificationTargetEncountered(&_expression, VerificationTargetType::Underflow, values.second < intType->minValue());
1234
4.98k
  else if (_op == Token::Add || _op == Token::Mul)
1235
4.98k
    verificationTargetEncountered(&_expression, VerificationTargetType::Overflow, values.second > intType->maxValue());
1236
0
  else
1237
4.98k
    solAssert(false, "");
1238
9.24k
  return values;
1239
10.0k
}
1240
1241
void CHC::resetSourceAnalysis()
1242
15.5k
{
1243
15.5k
  SMTEncoder::resetSourceAnalysis();
1244
1245
15.5k
  m_unprovedTargets.clear();
1246
15.5k
  m_invariants.clear();
1247
15.5k
  m_functionTargetIds.clear();
1248
15.5k
  m_verificationTargets.clear();
1249
15.5k
  m_queryPlaceholders.clear();
1250
15.5k
  m_callGraph.clear();
1251
15.5k
  m_summaries.clear();
1252
15.5k
  m_externalSummaries.clear();
1253
15.5k
  m_interfaces.clear();
1254
15.5k
  m_nondetInterfaces.clear();
1255
15.5k
  m_constructorSummaries.clear();
1256
15.5k
  m_contractInitializers.clear();
1257
15.5k
  Predicate::reset();
1258
15.5k
  ArraySlicePredicate::reset();
1259
15.5k
  m_blockCounter = 0;
1260
1261
15.5k
  solAssert(m_settings.solvers.smtlib2 || m_settings.solvers.eld || m_settings.solvers.z3);
1262
15.5k
  if (!m_interface)
1263
13.9k
  {
1264
13.9k
    if (m_settings.solvers.z3)
1265
0
      m_interface = std::make_unique<Z3CHCSmtLib2Interface>(
1266
0
        m_smtCallback,
1267
0
        m_settings.timeout,
1268
0
        m_settings.invariants != ModelCheckerInvariants::None()
1269
0
      );
1270
13.9k
    else if (m_settings.solvers.eld)
1271
0
      m_interface = std::make_unique<EldaricaCHCSmtLib2Interface>(
1272
0
        m_smtCallback,
1273
0
        m_settings.timeout,
1274
0
        m_settings.invariants != ModelCheckerInvariants::None()
1275
0
      );
1276
13.9k
    else
1277
13.9k
      m_interface = std::make_unique<CHCSmtLib2Interface>(m_smtlib2Responses, m_smtCallback, m_settings.timeout);
1278
13.9k
  }
1279
1280
15.5k
  auto smtlib2Interface = dynamic_cast<CHCSmtLib2Interface*>(m_interface.get());
1281
15.5k
  solAssert(smtlib2Interface);
1282
15.5k
  smtlib2Interface->reset();
1283
15.5k
  m_context.setSolver(smtlib2Interface);
1284
1285
15.5k
  m_context.reset();
1286
15.5k
  m_context.resetUniqueId();
1287
15.5k
  m_context.setAssertionAccumulation(false);
1288
15.5k
}
1289
1290
void CHC::resetContractAnalysis()
1291
16.2k
{
1292
16.2k
  m_stateVariables.clear();
1293
16.2k
  m_unknownFunctionCallSeen = false;
1294
16.2k
  m_breakDest = nullptr;
1295
16.2k
  m_continueDest = nullptr;
1296
16.2k
  m_returnDests.clear();
1297
16.2k
  errorFlag().resetIndex();
1298
16.2k
}
1299
1300
void CHC::eraseKnowledge()
1301
526
{
1302
526
  resetStorageVariables();
1303
526
  resetBalances();
1304
526
}
1305
1306
void CHC::clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function)
1307
202k
{
1308
202k
  SMTEncoder::clearIndices(_contract, _function);
1309
202k
  for (auto const* var: m_stateVariables)
1310
    /// SSA index 0 is reserved for state variables at the beginning
1311
    /// of the current transaction.
1312
92.8k
    m_context.variable(*var)->increaseIndex();
1313
202k
  if (_function)
1314
101k
  {
1315
101k
    for (auto const& var: _function->parameters() + _function->returnParameters())
1316
104k
      m_context.variable(*var)->increaseIndex();
1317
101k
    for (auto const& var: localVariablesIncludingModifiers(*_function, _contract))
1318
42.5k
      m_context.variable(*var)->increaseIndex();
1319
101k
  }
1320
1321
202k
  state().newState();
1322
202k
}
1323
1324
void CHC::setCurrentBlock(Predicate const& _block)
1325
152k
{
1326
152k
  if (m_context.solverStackHeight() > 0)
1327
152k
    m_context.popSolver();
1328
152k
  solAssert(m_currentContract, "");
1329
152k
  clearIndices(m_currentContract, m_currentFunction);
1330
152k
  m_context.pushSolver();
1331
152k
  m_currentBlock = predicate(_block);
1332
152k
}
1333
1334
std::set<unsigned> CHC::transactionVerificationTargetsIds(ASTNode const* _txRoot)
1335
28.6k
{
1336
28.6k
  std::set<unsigned> verificationTargetsIds;
1337
28.6k
  struct ASTNodeCompare: EncodingContext::IdCompare
1338
28.6k
  {
1339
28.6k
    bool operator<(ASTNodeCompare _other) const { return operator()(node, _other.node); }
1340
28.6k
    ASTNode const* node;
1341
28.6k
  };
1342
32.0k
  solidity::util::BreadthFirstSearch<ASTNodeCompare>{{{{}, _txRoot}}}.run([&](auto _node, auto&& _addChild) {
1343
32.0k
    verificationTargetsIds.insert(m_functionTargetIds[_node.node].begin(), m_functionTargetIds[_node.node].end());
1344
32.0k
    for (ASTNode const* called: m_callGraph[_node.node])
1345
4.98k
      _addChild({{}, called});
1346
32.0k
  });
1347
28.6k
  return verificationTargetsIds;
1348
28.6k
}
1349
1350
bool CHC::usesStaticCall(FunctionDefinition const* _funDef, FunctionType const* _funType)
1351
2.15k
{
1352
2.15k
  auto kind = _funType->kind();
1353
2.15k
  return (_funDef && (_funDef->stateMutability() == StateMutability::Pure || _funDef->stateMutability() == StateMutability::View)) || kind == FunctionType::Kind::BareStaticCall;
1354
2.15k
}
1355
1356
bool CHC::usesStaticCall(FunctionCall const& _funCall)
1357
907
{
1358
907
  FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
1359
907
  auto kind = funType.kind();
1360
907
  auto function = functionCallToDefinition(_funCall, currentScopeContract(), m_currentContract);
1361
907
  return (function && (function->stateMutability() == StateMutability::Pure || function->stateMutability() == StateMutability::View)) || kind == FunctionType::Kind::BareStaticCall;
1362
907
}
1363
1364
std::optional<CHC::CHCNatspecOption> CHC::natspecOptionFromString(std::string const& _option)
1365
110
{
1366
110
  static std::map<std::string, CHCNatspecOption> options{
1367
110
    {"abstract-function-nondet", CHCNatspecOption::AbstractFunctionNondet}
1368
110
  };
1369
110
  if (options.count(_option))
1370
4
    return options.at(_option);
1371
106
  return {};
1372
110
}
1373
1374
std::set<CHC::CHCNatspecOption> CHC::smtNatspecTags(FunctionDefinition const& _function)
1375
34.4k
{
1376
34.4k
  std::set<CHC::CHCNatspecOption> options;
1377
34.4k
  std::string smtStr = "custom:smtchecker";
1378
34.4k
  bool errorSeen = false;
1379
34.4k
  for (auto const& [tag, value]: _function.annotation().docTags)
1380
514
    if (tag == smtStr)
1381
110
    {
1382
110
      std::string const& content = value.content;
1383
110
      if (auto option = natspecOptionFromString(content))
1384
4
        options.insert(*option);
1385
106
      else if (!errorSeen)
1386
60
      {
1387
60
        errorSeen = true;
1388
60
        m_errorReporter.warning(3130_error, _function.location(), "Unknown option for \"" + smtStr + "\": \"" + content + "\"");
1389
60
      }
1390
110
    }
1391
34.4k
  return options;
1392
34.4k
}
1393
1394
bool CHC::abstractAsNondet(FunctionDefinition const& _function)
1395
34.4k
{
1396
34.4k
  return smtNatspecTags(_function).count(CHCNatspecOption::AbstractFunctionNondet);
1397
34.4k
}
1398
1399
SortPointer CHC::sort(FunctionDefinition const& _function)
1400
65.7k
{
1401
65.7k
  return functionBodySort(_function, m_currentContract, state());
1402
65.7k
}
1403
1404
bool CHC::encodeExternalCallsAsTrusted()
1405
79.7k
{
1406
79.7k
  return m_settings.externalCalls.isTrusted();
1407
79.7k
}
1408
1409
SortPointer CHC::sort(ASTNode const* _node)
1410
95.5k
{
1411
95.5k
  if (auto funDef = dynamic_cast<FunctionDefinition const*>(_node))
1412
65.7k
    return sort(*funDef);
1413
1414
29.7k
  solAssert(m_currentFunction, "");
1415
29.7k
  return functionBodySort(*m_currentFunction, m_currentContract, state());
1416
95.5k
}
1417
1418
Predicate const* CHC::createSymbolicBlock(SortPointer _sort, std::string const& _name, PredicateType _predType, ASTNode const* _node, ContractDefinition const* _contractContext)
1419
269k
{
1420
269k
  auto const* block = Predicate::create(_sort, _name, _predType, m_context, _node, _contractContext, m_scopes);
1421
269k
  m_interface->registerRelation(block->functor());
1422
269k
  return block;
1423
269k
}
1424
1425
void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
1426
16.1k
{
1427
16.1k
  for (auto const& node: _source.nodes())
1428
39.0k
    if (auto const* contract = dynamic_cast<ContractDefinition const*>(node.get()))
1429
16.8k
    {
1430
16.8k
      std::string suffix = contract->name() + "_" + std::to_string(contract->id());
1431
16.8k
      m_interfaces[contract] = createSymbolicBlock(interfaceSort(*contract, state()), "interface_" + uniquePrefix() + "_" + suffix, PredicateType::Interface, contract, contract);
1432
16.8k
      m_nondetInterfaces[contract] = createSymbolicBlock(nondetInterfaceSort(*contract, state()), "nondet_interface_" + uniquePrefix() + "_" + suffix, PredicateType::NondetInterface, contract, contract);
1433
16.8k
      m_constructorSummaries[contract] = createConstructorBlock(*contract, "summary_constructor");
1434
1435
16.8k
      for (auto const* var: stateVariablesIncludingInheritedAndPrivate(*contract))
1436
9.10k
        if (!m_context.knownVariable(*var))
1437
8.41k
          createVariable(*var);
1438
1439
      /// Base nondeterministic interface that allows
1440
      /// 0 steps to be taken, used as base for the inductive
1441
      /// rule for each function.
1442
16.8k
      auto const& iface = *m_nondetInterfaces.at(contract);
1443
16.8k
      addRule(smtutil::Expression::implies(errorFlag().currentValue() == 0, smt::nondetInterface(iface, *contract, m_context, 0, 0)), "base_nondet");
1444
1445
16.8k
      auto const& resolved = contractFunctions(*contract);
1446
16.8k
      for (auto const* function: contractFunctionsWithoutVirtual(*contract) + allFreeFunctions())
1447
18.2k
      {
1448
18.2k
        for (auto var: function->parameters())
1449
8.46k
          createVariable(*var);
1450
18.2k
        for (auto var: function->returnParameters())
1451
9.39k
          createVariable(*var);
1452
18.2k
        for (auto const* var: localVariablesIncludingModifiers(*function, contract))
1453
4.48k
          createVariable(*var);
1454
1455
18.2k
        m_summaries[contract].emplace(function, createSummaryBlock(*function, *contract));
1456
1457
18.2k
        if (
1458
18.2k
          !function->isConstructor() &&
1459
15.8k
          function->isPublic() &&
1460
          // Public library functions should have interfaces only for the libraries
1461
          // they're declared in.
1462
13.9k
          (!function->libraryFunction() || (function->scope() == contract)) &&
1463
13.7k
          resolved.count(function)
1464
18.2k
        )
1465
13.3k
        {
1466
13.3k
          m_externalSummaries[contract].emplace(function, createSummaryBlock(*function, *contract));
1467
1468
13.3k
          auto state1 = stateVariablesAtIndex(1, *contract);
1469
13.3k
          auto state2 = stateVariablesAtIndex(2, *contract);
1470
1471
13.3k
          auto errorPre = errorFlag().currentValue();
1472
13.3k
          auto nondetPre = smt::nondetInterface(iface, *contract, m_context, 0, 1);
1473
13.3k
          auto errorPost = errorFlag().increaseIndex();
1474
13.3k
          auto nondetPost = smt::nondetInterface(iface, *contract, m_context, 0, 2);
1475
1476
13.3k
          std::vector<smtutil::Expression> args =
1477
13.3k
            commonStateExpressions(errorPost, state().thisAddress()) +
1478
13.3k
            std::vector<smtutil::Expression>{state().tx(), state().state(1)};
1479
13.3k
          args += state1 +
1480
13.3k
            applyMap(function->parameters(), [this](auto _var) { return valueAtIndex(*_var, 0); }) +
1481
13.3k
            std::vector<smtutil::Expression>{state().state(2)} +
1482
13.3k
            state2 +
1483
13.3k
            applyMap(function->parameters(), [this](auto _var) { return valueAtIndex(*_var, 1); }) +
1484
13.3k
            applyMap(function->returnParameters(), [this](auto _var) { return valueAtIndex(*_var, 1); });
1485
1486
13.3k
          connectBlocks(nondetPre, nondetPost, errorPre == 0 && (*m_externalSummaries.at(contract).at(function))(args));
1487
13.3k
        }
1488
18.2k
      }
1489
16.8k
    }
1490
16.1k
}
1491
1492
void CHC::defineExternalFunctionInterface(FunctionDefinition const& _function, ContractDefinition const& _contract)
1493
12.7k
{
1494
  // Create a rule that represents an external call to this function.
1495
  // This contains more things than the function body itself,
1496
  // such as balance updates because of ``msg.value``.
1497
12.7k
  auto functionEntryBlock = createBlock(&_function, PredicateType::FunctionBlock);
1498
12.7k
  auto functionPred = predicate(*functionEntryBlock);
1499
12.7k
  addRule(functionPred, functionPred.name);
1500
12.7k
  setCurrentBlock(*functionEntryBlock);
1501
1502
12.7k
  m_context.addAssertion(initialConstraints(_contract, &_function));
1503
12.7k
  m_context.addAssertion(state().txTypeConstraints() && state().txFunctionConstraints(_function));
1504
1505
  // The contract may have received funds through a selfdestruct or
1506
  // block.coinbase, which do not trigger calls into the contract.
1507
  // So the only constraint we can add here is that the balance of
1508
  // the contract grows by at least `msg.value`.
1509
12.7k
  SymbolicIntVariable k{TypeProvider::uint256(), TypeProvider::uint256(), "funds_" + std::to_string(m_context.newUniqueId()), m_context};
1510
12.7k
  m_context.addAssertion(k.currentValue() >= state().txMember("msg.value"));
1511
  // Assume that address(this).balance cannot overflow.
1512
12.7k
  m_context.addAssertion(smt::symbolicUnknownConstraints(state().balance(state().thisAddress()) + k.currentValue(), TypeProvider::uint256()));
1513
12.7k
  state().addBalance(state().thisAddress(), k.currentValue());
1514
1515
12.7k
  if (encodeExternalCallsAsTrusted())
1516
0
  {
1517
    // If the contract has state variables that are addresses to other contracts,
1518
    // we need to encode the fact that those contracts may have been called in between
1519
    // transactions to _contract.
1520
    //
1521
    // We do that by adding nondet_interface constraints for those contracts,
1522
    // in the last line of this if block.
1523
    //
1524
    // If there are state variables of container types like structs or arrays
1525
    // that indirectly contain contract types, we havoc the state for simplicity,
1526
    // in the first part of this block.
1527
    // TODO: This could actually be supported.
1528
    // For structs: simply collect the SMT expressions of all the indirect contract type members.
1529
    // For arrays: more involved, needs to traverse the array symbolically and do the same for each contract.
1530
    // For mappings: way more complicated if the element type is a contract.
1531
0
    auto hasContractOrAddressSubType = [&](VariableDeclaration const* _var) -> bool {
1532
0
      bool foundContract = false;
1533
0
      solidity::util::BreadthFirstSearch<Type const*> bfs{{_var->type()}};
1534
0
      bfs.run([&](auto _type, auto&& _addChild) {
1535
0
        if (
1536
0
          _type->category() == Type::Category::Address ||
1537
0
          _type->category() == Type::Category::Contract
1538
0
        )
1539
0
        {
1540
0
          foundContract = true;
1541
0
          bfs.abort();
1542
0
        }
1543
0
        if (auto const* mapType = dynamic_cast<MappingType const*>(_type))
1544
0
          _addChild(mapType->valueType());
1545
0
        else if (auto const* arrayType = dynamic_cast<ArrayType const*>(_type))
1546
0
          _addChild(arrayType->baseType());
1547
0
        else if (auto const* structType = dynamic_cast<StructType const*>(_type))
1548
0
          for (auto const& member: structType->nativeMembers(nullptr))
1549
0
            _addChild(member.type);
1550
0
      });
1551
0
      return foundContract;
1552
0
    };
1553
0
    bool found = false;
1554
0
    for (auto var: m_currentContract->stateVariables())
1555
0
      if (
1556
0
        var->type()->category() != Type::Category::Address &&
1557
0
        var->type()->category() != Type::Category::Contract &&
1558
0
        hasContractOrAddressSubType(var)
1559
0
      )
1560
0
      {
1561
0
        found = true;
1562
0
        break;
1563
0
      }
1564
1565
0
    if (found)
1566
0
      state().newStorage();
1567
0
    else
1568
0
      addNondetCalls(*m_currentContract);
1569
0
  }
1570
1571
12.7k
  errorFlag().increaseIndex();
1572
12.7k
  m_context.addAssertion(summaryCall(_function));
1573
1574
12.7k
  connectBlocks(functionPred, externalSummary(_function));
1575
12.7k
}
1576
1577
void CHC::defineContractInitializer(ContractDefinition const& _contract, ContractDefinition const& _contextContract)
1578
18.0k
{
1579
18.0k
  m_contractInitializers[&_contextContract][&_contract] = createConstructorBlock(_contract, "contract_initializer");
1580
18.0k
  auto const& implicitConstructorPredicate = *createConstructorBlock(_contract, "contract_initializer_entry");
1581
1582
18.0k
  auto implicitFact = smt::constructor(implicitConstructorPredicate, m_context);
1583
18.0k
  addRule(smtutil::Expression::implies(initialConstraints(_contract), implicitFact), implicitFact.name);
1584
18.0k
  setCurrentBlock(implicitConstructorPredicate);
1585
1586
18.0k
  auto prevErrorDest = m_errorDest;
1587
18.0k
  m_errorDest = m_contractInitializers.at(&_contextContract).at(&_contract);
1588
18.0k
  for (auto var: _contract.stateVariables())
1589
9.05k
    if (var->value())
1590
2.89k
    {
1591
2.89k
      var->value()->accept(*this);
1592
2.89k
      assignment(*var, *var->value());
1593
2.89k
    }
1594
18.0k
  m_errorDest = prevErrorDest;
1595
1596
18.0k
  auto const& afterInit = *createConstructorBlock(_contract, "contract_initializer_after_init");
1597
18.0k
  connectBlocks(m_currentBlock, predicate(afterInit));
1598
18.0k
  setCurrentBlock(afterInit);
1599
1600
18.0k
  if (auto constructor = _contract.constructor())
1601
2.29k
  {
1602
2.29k
    errorFlag().increaseIndex();
1603
2.29k
    m_context.addAssertion(smt::functionCall(*m_summaries.at(&_contextContract).at(constructor), &_contextContract, m_context));
1604
2.29k
    connectBlocks(m_currentBlock, initializer(_contract, _contextContract), errorFlag().currentValue() > 0);
1605
2.29k
    m_context.addAssertion(errorFlag().currentValue() == 0);
1606
2.29k
  }
1607
1608
18.0k
  connectBlocks(m_currentBlock, initializer(_contract, _contextContract));
1609
18.0k
}
1610
1611
smtutil::Expression CHC::interface()
1612
29.0k
{
1613
29.0k
  solAssert(m_currentContract, "");
1614
29.0k
  return interface(*m_currentContract);
1615
29.0k
}
1616
1617
smtutil::Expression CHC::interface(ContractDefinition const& _contract)
1618
29.0k
{
1619
29.0k
  return ::interface(*m_interfaces.at(&_contract), _contract, m_context);
1620
29.0k
}
1621
1622
smtutil::Expression CHC::error()
1623
37.1k
{
1624
37.1k
  return (*m_errorPredicate)({});
1625
37.1k
}
1626
1627
smtutil::Expression CHC::initializer(ContractDefinition const& _contract, ContractDefinition const& _contractContext)
1628
20.3k
{
1629
20.3k
  return predicate(*m_contractInitializers.at(&_contractContext).at(&_contract));
1630
20.3k
}
1631
1632
smtutil::Expression CHC::summary(ContractDefinition const& _contract)
1633
34.2k
{
1634
34.2k
  return predicate(*m_constructorSummaries.at(&_contract));
1635
34.2k
}
1636
1637
smtutil::Expression CHC::summary(FunctionDefinition const& _function, ContractDefinition const& _contract)
1638
17.4k
{
1639
17.4k
  return smt::function(*m_summaries.at(&_contract).at(&_function), &_contract, m_context);
1640
17.4k
}
1641
1642
smtutil::Expression CHC::summary(FunctionDefinition const& _function)
1643
17.4k
{
1644
17.4k
  solAssert(m_currentContract, "");
1645
17.4k
  return summary(_function, *m_currentContract);
1646
17.4k
}
1647
1648
smtutil::Expression CHC::summaryCall(FunctionDefinition const& _function, ContractDefinition const& _contract)
1649
12.7k
{
1650
12.7k
  return smt::functionCall(*m_summaries.at(&_contract).at(&_function), &_contract, m_context);
1651
12.7k
}
1652
1653
smtutil::Expression CHC::summaryCall(FunctionDefinition const& _function)
1654
12.7k
{
1655
12.7k
  solAssert(m_currentContract, "");
1656
12.7k
  return summaryCall(_function, *m_currentContract);
1657
12.7k
}
1658
1659
smtutil::Expression CHC::externalSummary(FunctionDefinition const& _function, ContractDefinition const& _contract)
1660
25.5k
{
1661
25.5k
  return smt::function(*m_externalSummaries.at(&_contract).at(&_function), &_contract, m_context);
1662
25.5k
}
1663
1664
smtutil::Expression CHC::externalSummary(FunctionDefinition const& _function)
1665
25.5k
{
1666
25.5k
  solAssert(m_currentContract, "");
1667
25.5k
  return externalSummary(_function, *m_currentContract);
1668
25.5k
}
1669
1670
Predicate const* CHC::createBlock(ASTNode const* _node, PredicateType _predType, std::string const& _prefix)
1671
95.5k
{
1672
95.5k
  auto block = createSymbolicBlock(
1673
95.5k
    sort(_node),
1674
95.5k
    "block_" + uniquePrefix() + "_" + _prefix + predicateName(_node),
1675
95.5k
    _predType,
1676
95.5k
    _node,
1677
95.5k
    m_currentContract
1678
95.5k
  );
1679
1680
95.5k
  solAssert(m_currentFunction, "");
1681
95.5k
  return block;
1682
95.5k
}
1683
1684
Predicate const* CHC::createSummaryBlock(FunctionDefinition const& _function, ContractDefinition const& _contract, PredicateType _type)
1685
33.7k
{
1686
33.7k
  return createSymbolicBlock(
1687
33.7k
    functionSort(_function, &_contract, state()),
1688
33.7k
    "summary_" + uniquePrefix() + "_" + predicateName(&_function, &_contract),
1689
33.7k
    _type,
1690
33.7k
    &_function,
1691
33.7k
    &_contract
1692
33.7k
  );
1693
33.7k
}
1694
1695
Predicate const* CHC::createConstructorBlock(ContractDefinition const& _contract, std::string const& _prefix)
1696
87.2k
{
1697
87.2k
  return createSymbolicBlock(
1698
87.2k
    constructorSort(_contract, state()),
1699
87.2k
    _prefix + "_" + uniquePrefix() + "_" + contractSuffix(_contract),
1700
87.2k
    PredicateType::ConstructorSummary,
1701
87.2k
    &_contract,
1702
87.2k
    &_contract
1703
87.2k
  );
1704
87.2k
}
1705
1706
void CHC::createErrorBlock()
1707
18.5k
{
1708
18.5k
  m_errorPredicate = createSymbolicBlock(
1709
18.5k
    arity0FunctionSort(),
1710
18.5k
    "error_target_" + std::to_string(m_context.newUniqueId()),
1711
18.5k
    PredicateType::Error
1712
18.5k
  );
1713
18.5k
}
1714
1715
void CHC::connectBlocks(smtutil::Expression const& _from, smtutil::Expression const& _to, smtutil::Expression const& _constraints)
1716
233k
{
1717
233k
  smtutil::Expression edge = smtutil::Expression::implies(
1718
233k
    _from && m_context.assertions() && _constraints,
1719
233k
    _to
1720
233k
  );
1721
233k
  addRule(edge, _from.name + "_to_" + _to.name);
1722
233k
}
1723
1724
smtutil::Expression CHC::initialConstraints(ContractDefinition const& _contract, FunctionDefinition const* _function)
1725
64.2k
{
1726
64.2k
  smtutil::Expression conj = state().state() == state().state(0);
1727
64.2k
  conj = conj && errorFlag().currentValue() == 0;
1728
64.2k
  conj = conj && currentEqualInitialVarsConstraints(stateVariablesIncludingInheritedAndPrivate(_contract));
1729
1730
64.2k
  FunctionDefinition const* function = _function ? _function : _contract.constructor();
1731
64.2k
  if (function)
1732
34.0k
    conj = conj && currentEqualInitialVarsConstraints(applyMap(function->parameters(), [](auto&& _var) -> VariableDeclaration const* { return _var.get(); }));
1733
1734
64.2k
  return conj;
1735
64.2k
}
1736
1737
std::vector<smtutil::Expression> CHC::initialStateVariables()
1738
0
{
1739
0
  return stateVariablesAtIndex(0);
1740
0
}
1741
1742
std::vector<smtutil::Expression> CHC::stateVariablesAtIndex(unsigned _index)
1743
0
{
1744
0
  solAssert(m_currentContract, "");
1745
0
  return stateVariablesAtIndex(_index, *m_currentContract);
1746
0
}
1747
1748
std::vector<smtutil::Expression> CHC::stateVariablesAtIndex(unsigned _index, ContractDefinition const& _contract)
1749
26.6k
{
1750
26.6k
  return applyMap(
1751
26.6k
    SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract),
1752
26.6k
    [&](auto _var) { return valueAtIndex(*_var, _index); }
1753
26.6k
  );
1754
26.6k
}
1755
1756
std::vector<smtutil::Expression> CHC::currentStateVariables()
1757
984
{
1758
984
  solAssert(m_currentContract, "");
1759
984
  return currentStateVariables(*m_currentContract);
1760
984
}
1761
1762
std::vector<smtutil::Expression> CHC::currentStateVariables(ContractDefinition const& _contract)
1763
5.29k
{
1764
5.29k
  return applyMap(SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract), [this](auto _var) { return currentValue(*_var); });
1765
5.29k
}
1766
1767
smtutil::Expression CHC::currentEqualInitialVarsConstraints(std::vector<VariableDeclaration const*> const& _vars) const
1768
98.4k
{
1769
98.4k
  return fold(_vars, smtutil::Expression(true), [this](auto&& _conj, auto _var) {
1770
50.6k
    return std::move(_conj) && currentValue(*_var) == m_context.variable(*_var)->valueAtIndex(0);
1771
50.6k
  });
1772
98.4k
}
1773
1774
std::string CHC::predicateName(ASTNode const* _node, ContractDefinition const* _contract)
1775
129k
{
1776
129k
  std::string prefix;
1777
129k
  if (auto funDef = dynamic_cast<FunctionDefinition const*>(_node))
1778
99.4k
  {
1779
99.4k
    prefix += TokenTraits::toString(funDef->kind());
1780
99.4k
    if (!funDef->name().empty())
1781
89.9k
      prefix += "_" + funDef->name() + "_";
1782
99.4k
  }
1783
29.7k
  else if (m_currentFunction && !m_currentFunction->name().empty())
1784
26.6k
    prefix += m_currentFunction->name();
1785
1786
129k
  auto contract = _contract ? _contract : m_currentContract;
1787
129k
  solAssert(contract, "");
1788
129k
  return prefix + "_" + std::to_string(_node->id()) + "_" + std::to_string(contract->id());
1789
129k
}
1790
1791
smtutil::Expression CHC::predicate(Predicate const& _block)
1792
366k
{
1793
366k
  switch (_block.type())
1794
366k
  {
1795
12.7k
  case PredicateType::Interface:
1796
12.7k
    solAssert(m_currentContract, "");
1797
12.7k
    return ::interface(_block, *m_currentContract, m_context);
1798
157k
  case PredicateType::ConstructorSummary:
1799
157k
    return constructor(_block, m_context);
1800
38.1k
  case PredicateType::FunctionSummary:
1801
38.1k
  case PredicateType::InternalCall:
1802
38.1k
  case PredicateType::ExternalCallTrusted:
1803
38.1k
  case PredicateType::ExternalCallUntrusted:
1804
38.1k
    return smt::function(_block, m_currentContract, m_context);
1805
138k
  case PredicateType::FunctionBlock:
1806
157k
  case PredicateType::FunctionErrorBlock:
1807
157k
    solAssert(m_currentFunction, "");
1808
157k
    return functionBlock(_block, *m_currentFunction, m_currentContract, m_context);
1809
0
  case PredicateType::Error:
1810
0
    return _block({});
1811
0
  case PredicateType::NondetInterface:
1812
    // Nondeterministic interface predicates are handled differently.
1813
0
    solAssert(false, "");
1814
0
  case PredicateType::Custom:
1815
    // Custom rules are handled separately.
1816
0
    solAssert(false, "");
1817
366k
  }
1818
0
  solAssert(false, "");
1819
0
}
1820
1821
smtutil::Expression CHC::predicate(
1822
  FunctionDefinition const* _funDef,
1823
  std::optional<Expression const*> _boundArgumentCall,
1824
  FunctionType const* _funType,
1825
  std::vector<Expression const*> _arguments,
1826
  smtutil::Expression _contractAddressValue
1827
)
1828
2.25k
{
1829
2.25k
  solAssert(_funType, "");
1830
2.25k
  auto kind = _funType->kind();
1831
2.25k
  solAssert(kind == FunctionType::Kind::Internal || kind == FunctionType::Kind::External || kind == FunctionType::Kind::BareStaticCall, "");
1832
2.25k
  if (!_funDef)
1833
105
    return smtutil::Expression(true);
1834
1835
2.15k
  errorFlag().increaseIndex();
1836
1837
2.15k
  std::vector<smtutil::Expression> args =
1838
2.15k
    commonStateExpressions(errorFlag().currentValue(), _contractAddressValue) +
1839
2.15k
    std::vector<smtutil::Expression>{state().tx(), state().state()};
1840
1841
2.15k
  auto const* contract = _funDef->annotation().contract;
1842
2.15k
  auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts;
1843
2.15k
  solAssert(kind != FunctionType::Kind::Internal || _funDef->isFree() || (contract && contract->isLibrary()) || util::contains(hierarchy, contract), "");
1844
1845
2.15k
  if (kind == FunctionType::Kind::Internal)
1846
1.73k
    contract = m_currentContract;
1847
1848
2.15k
  args += currentStateVariables(*contract);
1849
2.15k
  args += symbolicArguments(_funDef->parameters(), _arguments, _boundArgumentCall);
1850
2.15k
  if (!usesStaticCall(_funDef, _funType))
1851
1.28k
  {
1852
1.28k
    state().newState();
1853
1.28k
    for (auto const& var: stateVariablesIncludingInheritedAndPrivate(*contract))
1854
652
      m_context.variable(*var)->increaseIndex();
1855
1.28k
  }
1856
2.15k
  args += std::vector<smtutil::Expression>{state().state()};
1857
2.15k
  args += currentStateVariables(*contract);
1858
1859
2.15k
  for (auto var: _funDef->parameters() + _funDef->returnParameters())
1860
2.99k
  {
1861
2.99k
    if (m_context.knownVariable(*var))
1862
2.99k
      m_context.variable(*var)->increaseIndex();
1863
0
    else
1864
0
      createVariable(*var);
1865
2.99k
    args.push_back(currentValue(*var));
1866
2.99k
  }
1867
1868
2.15k
  Predicate const& summary = *m_summaries.at(contract).at(_funDef);
1869
2.15k
  auto from = smt::function(summary, contract, m_context);
1870
2.15k
  Predicate const& callPredicate = *createSummaryBlock(
1871
2.15k
    *_funDef,
1872
2.15k
    *contract,
1873
2.15k
    kind == FunctionType::Kind::Internal ? PredicateType::InternalCall : PredicateType::ExternalCallTrusted
1874
2.15k
  );
1875
2.15k
  auto to = smt::function(callPredicate, contract, m_context);
1876
2.15k
  addRule(smtutil::Expression::implies(from, to), to.name);
1877
1878
2.15k
  return callPredicate(args);
1879
2.25k
}
1880
1881
void CHC::addRule(smtutil::Expression const& _rule, std::string const& _ruleName)
1882
336k
{
1883
336k
  m_interface->addRule(_rule, _ruleName);
1884
336k
}
1885
1886
CHCSolverInterface::QueryResult CHC::query(smtutil::Expression const& _query, langutil::SourceLocation const& _location)
1887
18.5k
{
1888
18.5k
  if (m_settings.printQuery)
1889
0
  {
1890
0
    auto smtLibInterface = dynamic_cast<CHCSmtLib2Interface*>(m_interface.get());
1891
0
    solAssert(smtLibInterface, "Requested to print queries but CHCSmtLib2Interface not available");
1892
0
    std::string smtLibCode = smtLibInterface->dumpQuery(_query);
1893
0
    m_errorReporter.info(
1894
0
      2339_error,
1895
0
      "CHC: Requested query:\n" + smtLibCode
1896
0
    );
1897
0
    return {.answer = CheckResult::UNKNOWN, .invariants = {}, .cex = {}};
1898
0
  }
1899
18.5k
  auto result = m_interface->query(_query);
1900
18.5k
  switch (result.answer)
1901
18.5k
  {
1902
0
  case CheckResult::SATISFIABLE:
1903
0
  case CheckResult::UNSATISFIABLE:
1904
18.5k
  case CheckResult::UNKNOWN:
1905
18.5k
    break;
1906
0
  case CheckResult::CONFLICTING:
1907
0
    m_errorReporter.warning(1988_error, _location, "CHC: At least two SMT solvers provided conflicting answers. Results might not be sound.");
1908
0
    break;
1909
0
  case CheckResult::ERROR:
1910
0
    m_errorReporter.warning(1218_error, _location, "CHC: Error during interaction with the solver.");
1911
0
    break;
1912
18.5k
  }
1913
18.5k
  return result;
1914
18.5k
}
1915
1916
void CHC::verificationTargetEncountered(
1917
  ASTNode const* const _errorNode,
1918
  VerificationTargetType _type,
1919
  smtutil::Expression const& _errorCondition
1920
)
1921
28.4k
{
1922
28.4k
  if (!m_settings.targets.has(_type))
1923
9.92k
    return;
1924
1925
18.5k
  if (!(m_currentContract || m_currentFunction))
1926
21
    return;
1927
1928
18.5k
  bool scopeIsFunction = m_currentFunction && !m_currentFunction->isConstructor();
1929
18.5k
  auto errorId = newErrorId();
1930
18.5k
  solAssert(m_verificationTargets.count(errorId) == 0, "Error ID is not unique!");
1931
18.5k
  m_verificationTargets.emplace(errorId, CHCVerificationTarget{{_type, _errorCondition, smtutil::Expression(true)}, errorId, _errorNode});
1932
18.5k
  if (scopeIsFunction)
1933
17.3k
    m_functionTargetIds[m_currentFunction].push_back(errorId);
1934
1.16k
  else
1935
1.16k
    m_functionTargetIds[m_currentContract].push_back(errorId);
1936
18.5k
  auto previousError = errorFlag().currentValue();
1937
18.5k
  errorFlag().increaseIndex();
1938
18.5k
  auto extendedErrorCondition = currentPathConditions() && _errorCondition;
1939
1940
18.5k
  Predicate const* localBlock = m_currentFunction ?
1941
18.4k
    createBlock(m_currentFunction, PredicateType::FunctionErrorBlock) :
1942
18.5k
    createConstructorBlock(*m_currentContract, "local_error");
1943
1944
18.5k
  auto pred = predicate(*localBlock);
1945
18.5k
  connectBlocks(
1946
18.5k
    m_currentBlock,
1947
18.5k
    pred,
1948
18.5k
    extendedErrorCondition && errorFlag().currentValue() == errorId
1949
18.5k
  );
1950
18.5k
  solAssert(m_errorDest, "");
1951
18.5k
  addRule(smtutil::Expression::implies(pred, predicate(*m_errorDest)), pred.name);
1952
1953
18.5k
  m_context.addAssertion(errorFlag().currentValue() == previousError);
1954
18.5k
}
1955
1956
std::pair<std::string, ErrorId> CHC::targetDescription(CHCVerificationTarget const& _target)
1957
18.5k
{
1958
18.5k
  if (_target.type == VerificationTargetType::PopEmptyArray)
1959
218
  {
1960
218
    solAssert(dynamic_cast<FunctionCall const*>(_target.errorNode), "");
1961
218
    return {"Empty array \"pop\"", 2529_error};
1962
218
  }
1963
18.2k
  else if (_target.type == VerificationTargetType::OutOfBounds)
1964
14.8k
  {
1965
14.8k
    solAssert(dynamic_cast<IndexAccess const*>(_target.errorNode), "");
1966
14.8k
    return {"Out of bounds access", 6368_error};
1967
14.8k
  }
1968
3.43k
  else if (
1969
3.43k
    _target.type == VerificationTargetType::Underflow ||
1970
3.43k
    _target.type == VerificationTargetType::Overflow
1971
3.43k
  )
1972
0
  {
1973
0
    auto const* expr = dynamic_cast<Expression const*>(_target.errorNode);
1974
0
    solAssert(expr, "");
1975
0
    auto const* intType = dynamic_cast<IntegerType const*>(expr->annotation().type);
1976
0
    if (!intType)
1977
0
      intType = TypeProvider::uint256();
1978
1979
0
    if (_target.type == VerificationTargetType::Underflow)
1980
0
      return {
1981
0
        "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ")",
1982
0
        3944_error
1983
0
      };
1984
1985
0
    return {
1986
0
      "Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ")",
1987
0
      4984_error
1988
0
    };
1989
0
  }
1990
3.43k
  else if (_target.type == VerificationTargetType::DivByZero)
1991
894
    return {"Division by zero", 4281_error};
1992
2.54k
  else if (_target.type == VerificationTargetType::Assert)
1993
2.45k
    return {"Assertion violation", 6328_error};
1994
84
  else if (_target.type == VerificationTargetType::Balance)
1995
84
    return {"Insufficient funds", 8656_error};
1996
0
  else
1997
84
    solAssert(false);
1998
18.5k
}
1999
2000
void CHC::checkVerificationTargets()
2001
15.5k
{
2002
  // The verification conditions have been collected per function where they have been encountered (m_verificationTargets).
2003
  // Also, all possible contexts in which an external function can be called has been recorded (m_queryPlaceholders).
2004
  // Here we combine every context in which an external function can be called with all possible verification conditions
2005
  // in its call graph. Each such combination forms a unique verification target.
2006
15.5k
  std::map<unsigned, std::vector<CHCQueryPlaceholder>> targetEntryPoints;
2007
15.5k
  for (auto const& [function, placeholders]: m_queryPlaceholders)
2008
28.6k
  {
2009
28.6k
    auto functionTargets = transactionVerificationTargetsIds(function);
2010
28.6k
    for (auto const& placeholder: placeholders)
2011
29.0k
      for (unsigned id: functionTargets)
2012
18.6k
        targetEntryPoints[id].push_back(placeholder);
2013
28.6k
  }
2014
2015
15.5k
  std::set<unsigned> checkedErrorIds;
2016
15.5k
  for (auto const& [targetId, placeholders]: targetEntryPoints)
2017
18.5k
  {
2018
18.5k
    auto const& target = m_verificationTargets.at(targetId);
2019
18.5k
    auto [errorType, errorReporterId] = targetDescription(target);
2020
2021
18.5k
    checkAndReportTarget(target, placeholders, errorReporterId, errorType + " happens here.", errorType + " might happen here.");
2022
18.5k
    checkedErrorIds.insert(target.errorId);
2023
18.5k
  }
2024
2025
15.5k
  auto toReport = m_unsafeTargets;
2026
15.5k
  if (m_settings.showUnproved)
2027
0
    for (auto const& [node, targets]: m_unprovedTargets)
2028
0
      for (auto const& [target, info]: targets)
2029
0
        toReport[node].emplace(target, info);
2030
2031
15.5k
  for (auto const& [node, targets]: toReport)
2032
0
    for (auto const& [target, info]: targets)
2033
0
      m_errorReporter.warning(
2034
0
        info.error,
2035
0
        info.location,
2036
0
        info.message
2037
0
      );
2038
2039
15.5k
  if (!m_settings.showUnproved && !m_unprovedTargets.empty())
2040
3.74k
    m_errorReporter.warning(
2041
3.74k
      5840_error,
2042
3.74k
      {},
2043
3.74k
      "CHC: " +
2044
3.74k
      std::to_string(m_unprovedTargets.size()) +
2045
3.74k
      " verification condition(s) could not be proved." +
2046
3.74k
      " Enable the model checker option \"show unproved\" to see all of them." +
2047
3.74k
      " Consider choosing a specific contract to be verified in order to reduce the solving problems." +
2048
3.74k
      " Consider increasing the timeout per query."
2049
3.74k
    );
2050
2051
15.5k
  if (!m_settings.showProvedSafe && !m_safeTargets.empty())
2052
0
  {
2053
0
    std::size_t provedSafeNum = 0;
2054
0
    for (auto&& [_, targets]: m_safeTargets)
2055
0
      provedSafeNum += targets.size();
2056
0
    m_errorReporter.info(
2057
0
      1391_error,
2058
0
      "CHC: " +
2059
0
      std::to_string(provedSafeNum) +
2060
0
      " verification condition(s) proved safe!" +
2061
0
      " Enable the model checker option \"show proved safe\" to see all of them."
2062
0
    );
2063
0
  }
2064
15.5k
  else if (m_settings.showProvedSafe)
2065
0
    for (auto const& [node, targets]: m_safeTargets)
2066
0
      for (auto const& target: targets)
2067
0
        m_provedSafeReporter.info(
2068
0
          9576_error,
2069
0
          node->location(),
2070
0
          "CHC: " +
2071
0
          targetDescription(target).first +
2072
0
          " check is safe!"
2073
0
        );
2074
2075
15.5k
  if (!m_settings.invariants.invariants.empty())
2076
15.5k
  {
2077
15.5k
    std::string msg;
2078
15.5k
    for (auto pred: m_invariants | ranges::views::keys)
2079
0
    {
2080
0
      ASTNode const* node = pred->programNode();
2081
0
      std::string what;
2082
0
      if (auto contract = dynamic_cast<ContractDefinition const*>(node))
2083
0
        what = contract->fullyQualifiedName();
2084
0
      else
2085
0
        solAssert(false, "");
2086
2087
0
      std::string invType;
2088
0
      if (pred->type() == PredicateType::Interface)
2089
0
        invType = "Contract invariant(s)";
2090
0
      else if (pred->type() == PredicateType::NondetInterface)
2091
0
        invType = "Reentrancy property(ies)";
2092
0
      else
2093
0
        solAssert(false, "");
2094
2095
0
      msg += invType + " for " + what + ":\n";
2096
0
      for (auto const& inv: m_invariants.at(pred))
2097
0
        msg += inv + "\n";
2098
0
    }
2099
15.5k
    if (msg.find("<errorCode>") != std::string::npos)
2100
0
    {
2101
0
      std::set<unsigned> seenErrors;
2102
0
      msg += "<errorCode> = 0 -> no errors\n";
2103
0
      for (auto const& [id, target]: m_verificationTargets)
2104
0
        if (!seenErrors.count(target.errorId))
2105
0
        {
2106
0
          seenErrors.insert(target.errorId);
2107
0
          std::string loc = std::string(m_charStreamProvider.charStream(*target.errorNode->location().sourceName).text(target.errorNode->location()));
2108
0
          msg += "<errorCode> = " + std::to_string(target.errorId) + " -> " + ModelCheckerTargets::targetTypeToString.at(target.type) + " at " + loc + "\n";
2109
2110
0
        }
2111
0
    }
2112
15.5k
    if (!msg.empty())
2113
0
      m_errorReporter.info(1180_error, msg);
2114
15.5k
  }
2115
2116
  // There can be targets in internal functions that are not reachable from the external interface.
2117
  // These are safe by definition and are not even checked by the CHC engine, but this information
2118
  // must still be reported safe by the BMC engine.
2119
15.5k
  std::set<unsigned> allErrorIds;
2120
15.5k
  for (auto const& entry: m_functionTargetIds)
2121
29.5k
    for (unsigned id: entry.second)
2122
18.5k
      allErrorIds.insert(id);
2123
2124
15.5k
  std::set<unsigned> unreachableErrorIds;
2125
15.5k
  set_difference(
2126
15.5k
    allErrorIds.begin(),
2127
15.5k
    allErrorIds.end(),
2128
15.5k
    checkedErrorIds.begin(),
2129
15.5k
    checkedErrorIds.end(),
2130
15.5k
    inserter(unreachableErrorIds, unreachableErrorIds.begin())
2131
15.5k
  );
2132
15.5k
  for (auto id: unreachableErrorIds)
2133
34
    m_safeTargets[m_verificationTargets.at(id).errorNode].insert(m_verificationTargets.at(id));
2134
15.5k
}
2135
2136
namespace
2137
{
2138
std::map<Predicate const*, std::set<std::string>> collectInvariants(
2139
  CHCSmtLib2Interface::Invariants const& _invariants,
2140
  std::set<Predicate const*> const& _predicates,
2141
  ModelCheckerInvariants const& _invariantsSetting
2142
)
2143
0
{
2144
0
  std::set<std::string> targets;
2145
0
  if (_invariantsSetting.has(InvariantType::Contract))
2146
0
    targets.insert("interface_");
2147
0
  if (_invariantsSetting.has(InvariantType::Reentrancy))
2148
0
    targets.insert("nondet_interface_");
2149
2150
0
  std::map<Predicate const*, std::set<std::string>> invariants;
2151
0
  for (auto const* pred: _predicates)
2152
0
  {
2153
0
    smtAssert(pred);
2154
0
    auto const& predName = pred->functor().name;
2155
0
    if (!_invariants.contains(predName))
2156
0
      continue;
2157
0
    if (ranges::none_of(targets, [&](auto const& _target) { return predName.starts_with(_target); }))
2158
0
      continue;
2159
2160
0
    smtAssert(pred->contextContract());
2161
2162
0
    auto const& [definition, formalArguments] = _invariants.at(predName);
2163
2164
0
    auto r = substitute(definition, pred->expressionSubstitution(formalArguments));
2165
    // No point in reporting true/false as invariants.
2166
0
    if (r.name != "true" && r.name != "false")
2167
0
      invariants[pred].insert(toSolidityStr(r));
2168
0
  }
2169
0
  return invariants;
2170
0
}
2171
} // namespace
2172
2173
void CHC::checkAndReportTarget(
2174
  CHCVerificationTarget const& _target,
2175
  std::vector<CHCQueryPlaceholder> const& _placeholders,
2176
  ErrorId _errorReporterId,
2177
  std::string _satMsg,
2178
  std::string _unknownMsg
2179
)
2180
18.5k
{
2181
18.5k
  if (m_unsafeTargets.count(_target.errorNode) && m_unsafeTargets.at(_target.errorNode).count(_target.type))
2182
0
    return;
2183
2184
18.5k
  createErrorBlock();
2185
18.5k
  for (auto const& placeholder: _placeholders)
2186
18.6k
    connectBlocks(
2187
18.6k
      placeholder.fromPredicate,
2188
18.6k
      error(),
2189
18.6k
      placeholder.constraints && placeholder.errorExpression == _target.errorId
2190
18.6k
    );
2191
18.5k
  auto const& location = _target.errorNode->location();
2192
18.5k
  auto [result, invariants, model] = query(error(), location);
2193
18.5k
  if (result == CheckResult::UNSATISFIABLE)
2194
0
  {
2195
0
    m_safeTargets[_target.errorNode].insert(_target);
2196
0
    std::set<Predicate const*> predicates;
2197
0
    for (auto const* pred: m_interfaces | ranges::views::values)
2198
0
      predicates.insert(pred);
2199
0
    for (auto const* pred: m_nondetInterfaces | ranges::views::values)
2200
0
      predicates.insert(pred);
2201
0
    std::map<Predicate const*, std::set<std::string>> invariantStrings = collectInvariants(invariants, predicates, m_settings.invariants);
2202
0
    for (auto pred: invariantStrings | ranges::views::keys)
2203
0
      m_invariants[pred] += std::move(invariantStrings.at(pred));
2204
0
  }
2205
18.5k
  else if (result == CheckResult::SATISFIABLE)
2206
0
  {
2207
0
    solAssert(!_satMsg.empty());
2208
0
    if (auto it = m_safeTargets.find(_target.errorNode); it != m_safeTargets.end())
2209
0
    {
2210
0
      std::erase_if(it->second, [&](auto const& target) { return target.type == _target.type; });
2211
0
      if (it->second.empty())
2212
0
        m_safeTargets.erase(it);
2213
0
    }
2214
0
    auto cex = generateCounterexample(model, error().name);
2215
0
    if (cex)
2216
0
      m_unsafeTargets[_target.errorNode][_target.type] = {
2217
0
        _errorReporterId,
2218
0
        location,
2219
0
        "CHC: " + _satMsg + "\nCounterexample:\n" + *cex
2220
0
      };
2221
0
    else
2222
0
      m_unsafeTargets[_target.errorNode][_target.type] = {
2223
0
        _errorReporterId,
2224
0
        location,
2225
0
        "CHC: " + _satMsg
2226
0
      };
2227
0
  }
2228
18.5k
  else if (!_unknownMsg.empty())
2229
18.5k
    m_unprovedTargets[_target.errorNode][_target.type] = {
2230
18.5k
      _errorReporterId,
2231
18.5k
      location,
2232
18.5k
      "CHC: " + _unknownMsg
2233
18.5k
    };
2234
18.5k
}
2235
2236
/**
2237
The counterexample DAG has the following properties:
2238
1) The root node represents the reachable error predicate.
2239
2) The root node has 1 or 2 children:
2240
  - One of them is the summary of the function that was called and led to that node.
2241
  If this is the only child, this function must be the constructor.
2242
  - If it has 2 children, the function is not the constructor and the other child is the interface node,
2243
  that is, it represents the state of the contract before the function described above was called.
2244
3) Interface nodes also have property 2.
2245
2246
We run a BFS on the DAG from the root node collecting the reachable function summaries from the given node.
2247
When a function summary is seen, the search continues with that summary as the new root for its subgraph.
2248
The result of the search is a callgraph containing:
2249
- Functions calls needed to reach the root node, that is, transaction entry points.
2250
- Functions called by other functions (internal calls or external calls/internal transactions).
2251
The BFS visit order and the shape of the DAG described in the previous paragraph guarantee that the order of
2252
the function summaries in the callgraph of the error node is the reverse transaction trace.
2253
2254
The first function summary seen contains the values for the state, input and output variables at the
2255
error point.
2256
*/
2257
std::optional<std::string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const& _graph, std::string const& _root)
2258
0
{
2259
0
  std::optional<unsigned> rootId;
2260
0
  for (auto const& [id, node]: _graph.nodes)
2261
0
    if (node.name == _root)
2262
0
    {
2263
0
      rootId = id;
2264
0
      break;
2265
0
    }
2266
0
  if (!rootId)
2267
0
    return {};
2268
2269
0
  std::vector<std::string> path;
2270
0
  std::string localState;
2271
2272
0
  auto callGraph = summaryCalls(_graph, *rootId);
2273
2274
0
  auto nodePred = [&](auto _node) { return Predicate::predicate(_graph.nodes.at(_node).name); };
2275
0
  auto nodeArgs = [&](auto _node) { return _graph.nodes.at(_node).arguments; };
2276
2277
0
  bool first = true;
2278
0
  for (auto summaryId: callGraph.at(*rootId))
2279
0
  {
2280
0
    CHCSolverInterface::CexNode const& summaryNode = _graph.nodes.at(summaryId);
2281
0
    Predicate const* summaryPredicate = Predicate::predicate(summaryNode.name);
2282
0
    auto const& summaryArgs = summaryNode.arguments;
2283
2284
0
    if (!summaryPredicate->programVariable())
2285
0
    {
2286
0
      auto stateVars = summaryPredicate->stateVariables();
2287
0
      solAssert(stateVars.has_value(), "");
2288
0
      auto stateValues = summaryPredicate->summaryStateValues(summaryArgs);
2289
0
      solAssert(stateValues.size() == stateVars->size(), "");
2290
2291
0
      if (first)
2292
0
      {
2293
0
        first = false;
2294
        /// Generate counterexample message local to the failed target.
2295
0
        localState = formatVariableModel(*stateVars, stateValues, ", ") + "\n";
2296
2297
0
        if (auto calledFun = summaryPredicate->programFunction())
2298
0
        {
2299
0
          auto inValues = summaryPredicate->summaryPostInputValues(summaryArgs);
2300
0
          auto const& inParams = calledFun->parameters();
2301
0
          if (auto inStr = formatVariableModel(inParams, inValues, "\n"); !inStr.empty())
2302
0
            localState += inStr + "\n";
2303
0
          auto outValues = summaryPredicate->summaryPostOutputValues(summaryArgs);
2304
0
          auto const& outParams = calledFun->returnParameters();
2305
0
          if (auto outStr = formatVariableModel(outParams, outValues, "\n"); !outStr.empty())
2306
0
            localState += outStr + "\n";
2307
2308
0
          std::optional<unsigned> localErrorId;
2309
0
          solidity::util::BreadthFirstSearch<unsigned> bfs{{summaryId}};
2310
0
          bfs.run([&](auto _nodeId, auto&& _addChild) {
2311
0
            auto const& children = _graph.edges.at(_nodeId);
2312
0
            if (
2313
0
              children.size() == 1 &&
2314
0
              nodePred(children.front())->isFunctionErrorBlock()
2315
0
            )
2316
0
            {
2317
0
              localErrorId = children.front();
2318
0
              bfs.abort();
2319
0
            }
2320
0
            ranges::for_each(children, _addChild);
2321
0
          });
2322
2323
0
          if (localErrorId.has_value())
2324
0
          {
2325
0
            auto const* localError = nodePred(*localErrorId);
2326
0
            solAssert(localError && localError->isFunctionErrorBlock(), "");
2327
0
            auto const [localValues, localVars] = localError->localVariableValues(nodeArgs(*localErrorId));
2328
0
            if (auto localStr = formatVariableModel(localVars, localValues, "\n"); !localStr.empty())
2329
0
              localState += localStr + "\n";
2330
0
          }
2331
0
        }
2332
0
      }
2333
0
      else
2334
0
      {
2335
0
        auto modelMsg = formatVariableModel(*stateVars, stateValues, ", ");
2336
        /// We report the state after every tx in the trace except for the last, which is reported
2337
        /// first in the code above.
2338
0
        if (!modelMsg.empty())
2339
0
          path.emplace_back("State: " + modelMsg);
2340
0
      }
2341
0
    }
2342
0
    std::string txCex = summaryPredicate->formatSummaryCall(summaryArgs, m_charStreamProvider);
2343
2344
0
    std::list<std::string> calls;
2345
0
    auto dfs = [&](unsigned parent, unsigned node, unsigned depth, auto&& _dfs) -> void {
2346
0
      auto pred = nodePred(node);
2347
0
      auto parentPred = nodePred(parent);
2348
0
      solAssert(pred && pred->isSummary(), "");
2349
0
      solAssert(parentPred && parentPred->isSummary(), "");
2350
0
      auto callTraceSize = calls.size();
2351
0
      if (!pred->isConstructorSummary())
2352
0
        for (unsigned v: callGraph[node])
2353
0
          _dfs(node, v, depth + 1, _dfs);
2354
2355
0
      bool appendTxVars = pred->isConstructorSummary() || pred->isFunctionSummary() || pred->isExternalCallUntrusted();
2356
2357
0
      calls.push_front(std::string(depth * 4, ' ') + pred->formatSummaryCall(nodeArgs(node), m_charStreamProvider, appendTxVars));
2358
0
      if (pred->isInternalCall())
2359
0
        calls.front() += " -- internal call";
2360
0
      else if (pred->isExternalCallTrusted())
2361
0
        calls.front() += " -- trusted external call";
2362
0
      else if (pred->isExternalCallUntrusted())
2363
0
      {
2364
0
        calls.front() += " -- untrusted external call";
2365
0
        if (calls.size() > callTraceSize + 1)
2366
0
          calls.front() += ", synthesized as:";
2367
0
      }
2368
0
      else if (pred->programVariable())
2369
0
      {
2370
0
        calls.front() += "-- action on external contract in state variable \"" + pred->programVariable()->name() + "\"";
2371
0
        if (calls.size() > callTraceSize + 1)
2372
0
          calls.front() += ", synthesized as:";
2373
0
      }
2374
0
      else if (pred->isFunctionSummary() && parentPred->isExternalCallUntrusted())
2375
0
        calls.front() += " -- reentrant call";
2376
0
    };
2377
0
    dfs(summaryId, summaryId, 0, dfs);
2378
0
    path.emplace_back(boost::algorithm::join(calls, "\n"));
2379
0
  }
2380
2381
0
  return localState + "\nTransaction trace:\n" + boost::algorithm::join(path | ranges::views::reverse, "\n");
2382
0
}
2383
2384
std::map<unsigned, std::vector<unsigned>> CHC::summaryCalls(CHCSolverInterface::CexGraph const& _graph, unsigned _root)
2385
0
{
2386
0
  std::map<unsigned, std::vector<unsigned>> calls;
2387
2388
0
  auto compare = [&](unsigned _a, unsigned _b) {
2389
0
    auto extract = [&](std::string const& _s) {
2390
      // We want to sort sibling predicates in the counterexample graph by their unique predicate id.
2391
      // For most predicates, this actually doesn't matter.
2392
      // The cases where this matters are internal and external function calls which have the form:
2393
      // summary_<CALLID>_<suffix>
2394
      // nondet_call_<CALLID>_<suffix>
2395
      // Those have the extra unique <CALLID> numbers based on the traversal order, and are necessary
2396
      // to infer the call order so that's shown property in the counterexample trace.
2397
      // For other predicates, we do not care.
2398
0
      auto beg = _s.data();
2399
0
      while (beg != _s.data() + _s.size() && !isDigit(*beg)) ++beg;
2400
0
      int result = -1;
2401
0
      static_cast<void>(std::from_chars(beg, _s.data() + _s.size(), result));
2402
0
      return result;
2403
0
    };
2404
0
    auto anum = extract(_graph.nodes.at(_a).name);
2405
0
    auto bnum = extract(_graph.nodes.at(_b).name);
2406
    // The second part of the condition is needed to ensure that two different predicates are not considered equal
2407
0
    return (anum > bnum) || (anum == bnum && _graph.nodes.at(_a).name > _graph.nodes.at(_b).name);
2408
0
  };
2409
2410
0
  std::queue<std::pair<unsigned, unsigned>> q;
2411
0
  q.push({_root, _root});
2412
0
  while (!q.empty())
2413
0
  {
2414
0
    auto [node, root] = q.front();
2415
0
    q.pop();
2416
2417
0
    Predicate const* nodePred = Predicate::predicate(_graph.nodes.at(node).name);
2418
0
    Predicate const* rootPred = Predicate::predicate(_graph.nodes.at(root).name);
2419
0
    if (nodePred->isSummary() && (
2420
0
      _root == root ||
2421
0
      nodePred->isInternalCall() ||
2422
0
      nodePred->isExternalCallTrusted() ||
2423
0
      nodePred->isExternalCallUntrusted() ||
2424
0
      rootPred->isExternalCallUntrusted() ||
2425
0
      rootPred->programVariable()
2426
0
    ))
2427
0
    {
2428
0
      calls[root].push_back(node);
2429
0
      root = node;
2430
0
    }
2431
0
    auto const& edges = _graph.edges.at(node);
2432
0
    for (unsigned v: std::set<unsigned, decltype(compare)>(begin(edges), end(edges), compare))
2433
0
      q.push({v, root});
2434
0
  }
2435
2436
0
  return calls;
2437
0
}
2438
2439
std::string CHC::cex2dot(CHCSolverInterface::CexGraph const& _cex)
2440
0
{
2441
0
  std::string dot = "digraph {\n";
2442
2443
0
  auto pred = [&](CHCSolverInterface::CexNode const& _node) {
2444
0
    std::vector<std::string> args = applyMap(
2445
0
      _node.arguments,
2446
0
      [&](auto const& arg) { return arg.name; }
2447
0
    );
2448
0
    return "\"" + _node.name + "(" + boost::algorithm::join(args, ", ") + ")\"";
2449
0
  };
2450
2451
0
  for (auto const& [u, vs]: _cex.edges)
2452
0
    for (auto v: vs)
2453
0
      dot += pred(_cex.nodes.at(v)) + " -> " + pred(_cex.nodes.at(u)) + "\n";
2454
2455
0
  dot += "}";
2456
0
  return dot;
2457
0
}
2458
2459
std::string CHC::uniquePrefix()
2460
250k
{
2461
250k
  return std::to_string(m_blockCounter++);
2462
250k
}
2463
2464
std::string CHC::contractSuffix(ContractDefinition const& _contract)
2465
87.2k
{
2466
87.2k
  return _contract.name() + "_" + std::to_string(_contract.id());
2467
87.2k
}
2468
2469
unsigned CHC::newErrorId()
2470
18.5k
{
2471
18.5k
  unsigned errorId = m_context.newUniqueId();
2472
  // We need to make sure the error id is not zero,
2473
  // because error id zero actually means no error in the CHC encoding.
2474
18.5k
  if (errorId == 0)
2475
3.58k
    errorId = m_context.newUniqueId();
2476
18.5k
  return errorId;
2477
18.5k
}
2478
2479
SymbolicIntVariable& CHC::errorFlag()
2480
342k
{
2481
342k
  return state().errorFlag();
2482
342k
}
2483
2484
void CHC::newTxConstraints(Expression const* _value)
2485
415
{
2486
415
  auto txOrigin = state().txMember("tx.origin");
2487
415
  state().newTx();
2488
  // set the transaction sender as this contract
2489
415
  m_context.addAssertion(state().txMember("msg.sender") == state().thisAddress());
2490
  // set the origin to be the current transaction origin
2491
415
  m_context.addAssertion(state().txMember("tx.origin") == txOrigin);
2492
2493
415
  if (_value)
2494
    // set the msg value
2495
7
    m_context.addAssertion(state().txMember("msg.value") == expr(*_value));
2496
415
}
2497
2498
frontend::Expression const* CHC::valueOption(FunctionCallOptions const* _options)
2499
907
{
2500
907
  if (_options)
2501
70
    for (auto&& [i, name]: _options->names() | ranges::views::enumerate)
2502
74
      if (name && *name == "value")
2503
34
        return _options->options().at(i).get();
2504
873
  return nullptr;
2505
907
}
2506
2507
void CHC::decreaseBalanceFromOptionsValue(Expression const& _value)
2508
34
{
2509
34
  state().addBalance(state().thisAddress(), 0 - expr(_value));
2510
34
}
2511
2512
std::vector<smtutil::Expression> CHC::commonStateExpressions(smtutil::Expression const& error, smtutil::Expression const& address)
2513
15.9k
{
2514
15.9k
  if (state().hasBytesConcatFunction())
2515
90
    return {error, address, state().abi(), state().bytesConcat(), state().crypto()};
2516
15.8k
  return {error, address, state().abi(), state().crypto()};
2517
15.9k
}