forked from ethereum/solidity
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBMC.h
241 lines (204 loc) · 8 KB
/
BMC.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Class that implements an SMT-based Bounded Model Checker (BMC).
* Traverses the AST such that:
* - Loops are unrolled
* - Internal function calls are inlined
* Creates verification targets for:
* - Underflow/Overflow
* - Constant conditions
* - Assertions
*/
#pragma once
#include <libsolidity/formal/EncodingContext.h>
#include <libsolidity/formal/ModelCheckerSettings.h>
#include <libsolidity/formal/SMTEncoder.h>
#include <libsolidity/interface/ReadFile.h>
#include <libsmtutil/BMCSolverInterface.h>
#include <liblangutil/UniqueErrorReporter.h>
#include <set>
#include <string>
#include <vector>
#include <stack>
using solidity::util::h256;
namespace solidity::langutil
{
class ErrorReporter;
struct ErrorId;
struct SourceLocation;
}
namespace solidity::frontend
{
class BMC: public SMTEncoder
{
public:
BMC(
smt::EncodingContext& _context,
langutil::UniqueErrorReporter& _errorReporter,
langutil::UniqueErrorReporter& _unsupportedErrorReporter,
langutil::ErrorReporter& _provedSafeReporter,
std::map<h256, std::string> const& _smtlib2Responses,
ReadCallback::Callback const& _smtCallback,
ModelCheckerSettings _settings,
langutil::CharStreamProvider const& _charStreamProvider
);
void analyze(SourceUnit const& _sources, std::map<ASTNode const*, std::set<VerificationTargetType>, smt::EncodingContext::IdCompare> _solvedTargets);
/// This is used if the SMT solver is not directly linked into this binary.
/// @returns a list of inputs to the SMT solver that were not part of the argument to
/// the constructor.
std::vector<std::string> unhandledQueries() { return m_interface->unhandledQueries(); }
/// @returns true if _funCall should be inlined, otherwise false.
/// @param _scopeContract The contract that contains the current function being analyzed.
/// @param _contextContract The most derived contract, currently being analyzed.
static bool shouldInlineFunctionCall(
FunctionCall const& _funCall,
ContractDefinition const* _scopeContract,
ContractDefinition const* _contextContract
);
private:
/// AST visitors.
/// Only nodes that lead to verification targets being built
/// or checked are visited.
//@{
bool visit(ContractDefinition const& _node) override;
void endVisit(ContractDefinition const& _node) override;
bool visit(FunctionDefinition const& _node) override;
void endVisit(FunctionDefinition const& _node) override;
bool visit(IfStatement const& _node) override;
bool visit(Conditional const& _node) override;
bool visit(WhileStatement const& _node) override;
bool visit(ForStatement const& _node) override;
void endVisit(UnaryOperation const& _node) override;
void endVisit(BinaryOperation const& _node) override;
void endVisit(FunctionCall const& _node) override;
void endVisit(Return const& _node) override;
bool visit(TryStatement const& _node) override;
bool visit(Break const& _node) override;
bool visit(Continue const& _node) override;
//@}
/// Visitor helpers.
//@{
void visitAssert(FunctionCall const& _funCall);
void visitRequire(FunctionCall const& _funCall);
void visitAddMulMod(FunctionCall const& _funCall) override;
void assignment(smt::SymbolicVariable& _symVar, smtutil::Expression const& _value) override;
/// Visits the FunctionDefinition of the called function
/// if available and inlines the return value.
void inlineFunctionCall(FunctionCall const& _funCall);
void inlineFunctionCall(
FunctionDefinition const* _funDef,
Expression const& _callStackExpr,
std::optional<Expression const*> _calledExpr,
std::vector<Expression const*> const& _arguments
);
/// Inlines if the function call is internal or external to `this`.
/// Erases knowledge about state variables if external.
void internalOrExternalFunctionCall(FunctionCall const& _funCall);
/// Creates underflow/overflow verification targets.
std::pair<smtutil::Expression, smtutil::Expression> arithmeticOperation(
Token _op,
smtutil::Expression const& _left,
smtutil::Expression const& _right,
Type const* _commonType,
Expression const& _expression
) override;
void reset();
std::pair<std::vector<smtutil::Expression>, std::vector<std::string>> modelExpressions();
//@}
/// Verification targets.
//@{
struct BMCVerificationTarget: VerificationTarget
{
Expression const* expression;
std::vector<CallStackEntry> callStack;
std::pair<std::vector<smtutil::Expression>, std::vector<std::string>> modelExpressions;
friend bool operator<(BMCVerificationTarget const& _a, BMCVerificationTarget const& _b)
{
if (_a.expression->id() == _b.expression->id())
return _a.type < _b.type;
else
return _a.expression->id() < _b.expression->id();
}
};
std::string targetDescription(BMCVerificationTarget const& _target);
void checkVerificationTargets();
void checkVerificationTarget(BMCVerificationTarget& _target);
void checkConstantCondition(BMCVerificationTarget& _target);
void checkUnderflow(BMCVerificationTarget& _target);
void checkOverflow(BMCVerificationTarget& _target);
void checkDivByZero(BMCVerificationTarget& _target);
void checkBalance(BMCVerificationTarget& _target);
void checkAssert(BMCVerificationTarget& _target);
void addVerificationTarget(
VerificationTargetType _type,
smtutil::Expression const& _value,
Expression const* _expression
);
//@}
/// Solver related.
//@{
/// Check that a condition can be satisfied.
void checkCondition(
BMCVerificationTarget const& _target,
smtutil::Expression _condition,
std::vector<CallStackEntry> const& _callStack,
std::pair<std::vector<smtutil::Expression>, std::vector<std::string>> const& _modelExpressions,
langutil::SourceLocation const& _location,
langutil::ErrorId _errorHappens,
langutil::ErrorId _errorMightHappen,
std::string const& _additionalValueName = "",
smtutil::Expression const* _additionalValue = nullptr
);
/// Checks that a boolean condition is not constant. Do not warn if the expression
/// is a literal constant.
void checkBooleanNotConstant(
Expression const& _condition,
smtutil::Expression const& _constraints,
smtutil::Expression const& _value,
std::vector<CallStackEntry> const& _callStack
);
std::pair<smtutil::CheckResult, std::vector<std::string>>
checkSatisfiableAndGenerateModel(std::vector<smtutil::Expression> const& _expressionsToEvaluate);
smtutil::CheckResult checkSatisfiable();
//@}
smtutil::Expression mergeVariablesFromLoopCheckpoints();
bool isInsideLoop() const;
std::unique_ptr<smtutil::BMCSolverInterface> m_interface;
/// Flags used for better warning messages.
bool m_loopExecutionHappened = false;
bool m_externalFunctionCallHappened = false;
std::vector<BMCVerificationTarget> m_verificationTargets;
/// Targets proved safe by this engine.
std::map<ASTNode const*, std::set<BMCVerificationTarget>, smt::EncodingContext::IdCompare> m_safeTargets;
/// Targets that were already proven before this engine started.
std::map<ASTNode const*, std::set<VerificationTargetType>, smt::EncodingContext::IdCompare> m_solvedTargets;
/// Number of verification conditions that could not be proved.
size_t m_unprovedAmt = 0;
enum class LoopControlKind
{
Continue,
Break
};
// Current path conditions and SSA indices for break or continue statement
struct LoopControl {
LoopControlKind kind;
smtutil::Expression pathConditions;
VariableIndices variableIndices;
};
// Loop control statements for every loop
std::stack<std::vector<LoopControl>> m_loopCheckpoints;
};
}