Coverage Report

Created: 2025-09-04 07:34

/src/solidity/libsolidity/formal/ModelChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
  This file is part of solidity.
3
4
  solidity is free software: you can redistribute it and/or modify
5
  it under the terms of the GNU General Public License as published by
6
  the Free Software Foundation, either version 3 of the License, or
7
  (at your option) any later version.
8
9
  solidity is distributed in the hope that it will be useful,
10
  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
  GNU General Public License for more details.
13
14
  You should have received a copy of the GNU General Public License
15
  along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
// SPDX-License-Identifier: GPL-3.0
18
19
#include <libsolidity/formal/ModelChecker.h>
20
21
#include <boost/version.hpp>
22
#if (BOOST_VERSION < 108800)
23
#include <boost/process.hpp>
24
#else
25
#define BOOST_PROCESS_VERSION 1
26
#include <boost/process/v1/search_path.hpp>
27
#endif
28
29
#include <range/v3/algorithm/any_of.hpp>
30
#include <range/v3/view.hpp>
31
32
using namespace solidity;
33
using namespace solidity::util;
34
using namespace solidity::langutil;
35
using namespace solidity::frontend;
36
using namespace solidity::smtutil;
37
38
ModelChecker::ModelChecker(
39
  ErrorReporter& _errorReporter,
40
  langutil::CharStreamProvider const& _charStreamProvider,
41
  std::map<h256, std::string> const& _smtlib2Responses,
42
  ModelCheckerSettings _settings,
43
  ReadCallback::Callback const& _smtCallback
44
):
45
  m_errorReporter(_errorReporter),
46
  m_provedSafeReporter(m_provedSafeLogs),
47
  m_settings(std::move(_settings)),
48
  m_context(),
49
  m_bmc(m_context, m_uniqueErrorReporter, m_unsupportedErrorReporter, m_provedSafeReporter, _smtlib2Responses, _smtCallback, m_settings, _charStreamProvider),
50
  m_chc(m_context, m_uniqueErrorReporter, m_unsupportedErrorReporter, m_provedSafeReporter, _smtlib2Responses, _smtCallback, m_settings, _charStreamProvider)
51
15.2k
{
52
15.2k
}
53
54
// TODO This should be removed for 0.9.0.
55
bool ModelChecker::isPragmaPresent(std::vector<std::shared_ptr<SourceUnit>> const& _sources)
56
15.2k
{
57
15.2k
  return ranges::any_of(_sources, [](auto _source) {
58
15.2k
    return _source && _source->annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker);
59
15.2k
  });
60
15.2k
}
61
62
void ModelChecker::checkRequestedSourcesAndContracts(std::vector<std::shared_ptr<SourceUnit>> const& _sources)
63
15.2k
{
64
15.2k
  std::map<std::string, std::set<std::string>> exist;
65
15.2k
  for (auto const& source: _sources)
66
16.7k
    for (auto node: source->nodes())
67
40.6k
      if (auto contract = std::dynamic_pointer_cast<ContractDefinition>(node))
68
17.7k
        exist[contract->sourceUnitName()].insert(contract->name());
69
70
  // Requested sources
71
15.2k
  for (auto const& sourceName: m_settings.contracts.contracts | ranges::views::keys)
72
0
  {
73
0
    if (!exist.count(sourceName))
74
0
    {
75
0
      m_uniqueErrorReporter.warning(
76
0
        9134_error,
77
0
        SourceLocation(),
78
0
        "Requested source \"" + sourceName + "\" does not exist."
79
0
      );
80
0
      continue;
81
0
    }
82
0
    auto const& source = exist.at(sourceName);
83
    // Requested contracts in source `s`.
84
0
    for (auto const& contract: m_settings.contracts.contracts.at(sourceName))
85
0
      if (!source.count(contract))
86
0
        m_uniqueErrorReporter.warning(
87
0
          7400_error,
88
0
          SourceLocation(),
89
0
          "Requested contract \"" + contract + "\" does not exist in source \"" + sourceName + "\"."
90
0
        );
91
0
  }
92
15.2k
}
93
94
void ModelChecker::analyze(SourceUnit const& _source)
95
16.7k
{
96
  // TODO This should be removed for 0.9.0.
97
16.7k
  if (_source.annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker))
98
16.1k
  {
99
16.1k
    PragmaDirective const* smtPragma = nullptr;
100
16.1k
    for (auto node: _source.nodes())
101
39.0k
      if (auto pragma = std::dynamic_pointer_cast<PragmaDirective>(node))
102
19.2k
        if (
103
19.2k
          pragma->literals().size() >= 2 &&
104
19.2k
          pragma->literals().at(1) == "SMTChecker"
105
19.2k
        )
106
16.1k
        {
107
16.1k
          smtPragma = pragma.get();
108
16.1k
          break;
109
16.1k
        }
110
16.1k
    solAssert(smtPragma, "");
111
16.1k
    m_uniqueErrorReporter.warning(
112
16.1k
      5523_error,
113
16.1k
      smtPragma->location(),
114
16.1k
      "The SMTChecker pragma has been deprecated and will be removed in the future. "
115
16.1k
      "Please use the \"model checker engine\" compiler setting to activate the SMTChecker instead. "
116
16.1k
      "If the pragma is enabled, all engines will be used."
117
16.1k
    );
118
16.1k
  }
119
120
16.7k
  if (m_settings.engine.none())
121
590
    return;
122
123
16.2k
  if (m_settings.engine.chc)
124
16.2k
    m_chc.analyze(_source);
125
126
16.2k
  std::map<ASTNode const*, std::set<VerificationTargetType>, smt::EncodingContext::IdCompare> solvedTargets;
127
128
16.2k
  for (auto const& [node, targets]: m_chc.safeTargets())
129
49
    for (auto const& target: targets)
130
49
      solvedTargets[node].insert(target.type);
131
132
16.2k
  for (auto const& [node, targets]: m_chc.unsafeTargets())
133
0
    solvedTargets[node] += targets | ranges::views::keys;
134
135
16.2k
  if (m_settings.engine.bmc)
136
16.2k
    m_bmc.analyze(_source, solvedTargets);
137
138
16.2k
  if (m_settings.showUnsupported)
139
0
  {
140
0
    m_errorReporter.append(m_unsupportedErrorReporter.errors());
141
0
    m_unsupportedErrorReporter.clear();
142
0
  }
143
16.2k
  else if (!m_unsupportedErrorReporter.errors().empty())
144
3.18k
    m_errorReporter.warning(
145
3.18k
      5724_error,
146
3.18k
      {},
147
3.18k
      "SMTChecker: " +
148
3.18k
      std::to_string(m_unsupportedErrorReporter.errors().size()) +
149
3.18k
      " unsupported language feature(s)."
150
3.18k
      " Enable the model checker option \"show unsupported\" to see all of them."
151
3.18k
    );
152
153
16.2k
  m_errorReporter.append(m_uniqueErrorReporter.errors());
154
16.2k
  m_uniqueErrorReporter.clear();
155
156
16.2k
  if (m_settings.showProvedSafe)
157
0
  {
158
0
    m_errorReporter.append(m_provedSafeReporter.errors());
159
0
    m_provedSafeReporter.clear();
160
0
  }
161
16.2k
}
162
163
std::vector<std::string> ModelChecker::unhandledQueries()
164
15.2k
{
165
15.2k
  return m_bmc.unhandledQueries() + m_chc.unhandledQueries();
166
15.2k
}
167
168
SMTSolverChoice ModelChecker::availableSolvers()
169
14.6k
{
170
14.6k
  smtutil::SMTSolverChoice available = smtutil::SMTSolverChoice::SMTLIB2();
171
14.6k
  available.eld = !boost::process::search_path("eld").empty();
172
14.6k
  available.cvc5 = !boost::process::search_path("cvc5").empty();
173
#ifdef EMSCRIPTEN_BUILD
174
  available.z3 = true;
175
#else
176
14.6k
  available.z3 = !boost::process::search_path("z3").empty();
177
14.6k
#endif
178
14.6k
  return available;
179
14.6k
}
180
181
SMTSolverChoice ModelChecker::checkRequestedSolvers(SMTSolverChoice _enabled, ErrorReporter& _errorReporter)
182
14.6k
{
183
14.6k
  SMTSolverChoice availableSolvers{ModelChecker::availableSolvers()};
184
185
14.6k
  if (_enabled.cvc5 && !availableSolvers.cvc5)
186
14.6k
  {
187
14.6k
    _enabled.cvc5 = false;
188
14.6k
    _errorReporter.warning(
189
14.6k
      4902_error,
190
14.6k
      SourceLocation(),
191
14.6k
      "Solver cvc5 was selected for SMTChecker but it is not available."
192
14.6k
    );
193
14.6k
  }
194
195
14.6k
  if (_enabled.eld && !availableSolvers.eld)
196
14.6k
  {
197
14.6k
    _enabled.eld = false;
198
14.6k
    _errorReporter.warning(
199
14.6k
      4458_error,
200
14.6k
      SourceLocation(),
201
14.6k
#if defined(__linux) || defined(__APPLE__)
202
14.6k
      "Solver Eldarica was selected for SMTChecker but it was not found in the system."
203
#else
204
      "Solver Eldarica was selected for SMTChecker but it is only supported on Linux and MacOS."
205
#endif
206
14.6k
    );
207
14.6k
  }
208
209
14.6k
  if (_enabled.z3 && !availableSolvers.z3)
210
14.6k
  {
211
14.6k
    _enabled.z3 = false;
212
14.6k
    _errorReporter.warning(
213
14.6k
      8158_error,
214
14.6k
      SourceLocation(),
215
14.6k
      "Solver z3 was selected for SMTChecker but it is not available."
216
14.6k
    );
217
14.6k
  }
218
219
14.6k
  return _enabled;
220
14.6k
}