1 //== SMTConstraintManager.h -------------------------------------*- C++ -*--==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines a SMT generic API, which will be the base class for
10 // every SMT solver specific class.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONSTRAINTMANAGER_H
15 #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONSTRAINTMANAGER_H
16
17 #include "clang/Basic/JsonSupport.h"
18 #include "clang/Basic/TargetInfo.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h"
21 #include <optional>
22
23 typedef llvm::ImmutableSet<
24 std::pair<clang::ento::SymbolRef, const llvm::SMTExpr *>>
25 ConstraintSMTType;
REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintSMT,ConstraintSMTType)26 REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintSMT, ConstraintSMTType)
27
28 namespace clang {
29 namespace ento {
30
31 class SMTConstraintManager : public clang::ento::SimpleConstraintManager {
32 mutable llvm::SMTSolverRef Solver = llvm::CreateZ3Solver();
33
34 public:
35 SMTConstraintManager(clang::ento::ExprEngine *EE,
36 clang::ento::SValBuilder &SB)
37 : SimpleConstraintManager(EE, SB) {
38 Solver->setBoolParam("model", true); // Enable model finding
39 Solver->setUnsignedParam("timeout", 15000 /*milliseconds*/);
40 }
41 virtual ~SMTConstraintManager() = default;
42
43 //===------------------------------------------------------------------===//
44 // Implementation for interface from SimpleConstraintManager.
45 //===------------------------------------------------------------------===//
46
47 ProgramStateRef assumeSym(ProgramStateRef State, SymbolRef Sym,
48 bool Assumption) override {
49 ASTContext &Ctx = getBasicVals().getContext();
50
51 QualType RetTy;
52 bool hasComparison;
53
54 llvm::SMTExprRef Exp =
55 SMTConv::getExpr(Solver, Ctx, Sym, &RetTy, &hasComparison);
56
57 // Create zero comparison for implicit boolean cast, with reversed
58 // assumption
59 if (!hasComparison && !RetTy->isBooleanType())
60 return assumeExpr(
61 State, Sym,
62 SMTConv::getZeroExpr(Solver, Ctx, Exp, RetTy, !Assumption));
63
64 return assumeExpr(State, Sym, Assumption ? Exp : Solver->mkNot(Exp));
65 }
66
67 ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, SymbolRef Sym,
68 const llvm::APSInt &From,
69 const llvm::APSInt &To,
70 bool InRange) override {
71 ASTContext &Ctx = getBasicVals().getContext();
72 return assumeExpr(
73 State, Sym, SMTConv::getRangeExpr(Solver, Ctx, Sym, From, To, InRange));
74 }
75
76 ProgramStateRef assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym,
77 bool Assumption) override {
78 // Skip anything that is unsupported
79 return State;
80 }
81
82 //===------------------------------------------------------------------===//
83 // Implementation for interface from ConstraintManager.
84 //===------------------------------------------------------------------===//
85
86 ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override {
87 ASTContext &Ctx = getBasicVals().getContext();
88
89 QualType RetTy;
90 // The expression may be casted, so we cannot call getZ3DataExpr() directly
91 llvm::SMTExprRef VarExp = SMTConv::getExpr(Solver, Ctx, Sym, &RetTy);
92 llvm::SMTExprRef Exp =
93 SMTConv::getZeroExpr(Solver, Ctx, VarExp, RetTy, /*Assumption=*/true);
94
95 // Negate the constraint
96 llvm::SMTExprRef NotExp =
97 SMTConv::getZeroExpr(Solver, Ctx, VarExp, RetTy, /*Assumption=*/false);
98
99 ConditionTruthVal isSat = checkModel(State, Sym, Exp);
100 ConditionTruthVal isNotSat = checkModel(State, Sym, NotExp);
101
102 // Zero is the only possible solution
103 if (isSat.isConstrainedTrue() && isNotSat.isConstrainedFalse())
104 return true;
105
106 // Zero is not a solution
107 if (isSat.isConstrainedFalse() && isNotSat.isConstrainedTrue())
108 return false;
109
110 // Zero may be a solution
111 return ConditionTruthVal();
112 }
113
114 const llvm::APSInt *getSymVal(ProgramStateRef State,
115 SymbolRef Sym) const override {
116 BasicValueFactory &BVF = getBasicVals();
117 ASTContext &Ctx = BVF.getContext();
118
119 if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) {
120 QualType Ty = Sym->getType();
121 assert(!Ty->isRealFloatingType());
122 llvm::APSInt Value(Ctx.getTypeSize(Ty),
123 !Ty->isSignedIntegerOrEnumerationType());
124
125 // TODO: this should call checkModel so we can use the cache, however,
126 // this method tries to get the interpretation (the actual value) from
127 // the solver, which is currently not cached.
128
129 llvm::SMTExprRef Exp = SMTConv::fromData(Solver, Ctx, SD);
130
131 Solver->reset();
132 addStateConstraints(State);
133
134 // Constraints are unsatisfiable
135 std::optional<bool> isSat = Solver->check();
136 if (!isSat || !*isSat)
137 return nullptr;
138
139 // Model does not assign interpretation
140 if (!Solver->getInterpretation(Exp, Value))
141 return nullptr;
142
143 // A value has been obtained, check if it is the only value
144 llvm::SMTExprRef NotExp = SMTConv::fromBinOp(
145 Solver, Exp, BO_NE,
146 Ty->isBooleanType() ? Solver->mkBoolean(Value.getBoolValue())
147 : Solver->mkBitvector(Value, Value.getBitWidth()),
148 /*isSigned=*/false);
149
150 Solver->addConstraint(NotExp);
151
152 std::optional<bool> isNotSat = Solver->check();
153 if (!isNotSat || *isNotSat)
154 return nullptr;
155
156 // This is the only solution, store it
157 return &BVF.getValue(Value);
158 }
159
160 if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) {
161 SymbolRef CastSym = SC->getOperand();
162 QualType CastTy = SC->getType();
163 // Skip the void type
164 if (CastTy->isVoidType())
165 return nullptr;
166
167 const llvm::APSInt *Value;
168 if (!(Value = getSymVal(State, CastSym)))
169 return nullptr;
170 return &BVF.Convert(SC->getType(), *Value);
171 }
172
173 if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
174 const llvm::APSInt *LHS, *RHS;
175 if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) {
176 LHS = getSymVal(State, SIE->getLHS());
177 RHS = &SIE->getRHS();
178 } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) {
179 LHS = &ISE->getLHS();
180 RHS = getSymVal(State, ISE->getRHS());
181 } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) {
182 // Early termination to avoid expensive call
183 LHS = getSymVal(State, SSM->getLHS());
184 RHS = LHS ? getSymVal(State, SSM->getRHS()) : nullptr;
185 } else {
186 llvm_unreachable("Unsupported binary expression to get symbol value!");
187 }
188
189 if (!LHS || !RHS)
190 return nullptr;
191
192 llvm::APSInt ConvertedLHS, ConvertedRHS;
193 QualType LTy, RTy;
194 std::tie(ConvertedLHS, LTy) = SMTConv::fixAPSInt(Ctx, *LHS);
195 std::tie(ConvertedRHS, RTy) = SMTConv::fixAPSInt(Ctx, *RHS);
196 SMTConv::doIntTypeConversion<llvm::APSInt, &SMTConv::castAPSInt>(
197 Solver, Ctx, ConvertedLHS, LTy, ConvertedRHS, RTy);
198 return BVF.evalAPSInt(BSE->getOpcode(), ConvertedLHS, ConvertedRHS);
199 }
200
201 llvm_unreachable("Unsupported expression to get symbol value!");
202 }
203
204 ProgramStateRef removeDeadBindings(ProgramStateRef State,
205 SymbolReaper &SymReaper) override {
206 auto CZ = State->get<ConstraintSMT>();
207 auto &CZFactory = State->get_context<ConstraintSMT>();
208
209 for (const auto &Entry : CZ) {
210 if (SymReaper.isDead(Entry.first))
211 CZ = CZFactory.remove(CZ, Entry);
212 }
213
214 return State->set<ConstraintSMT>(CZ);
215 }
216
217 void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n",
218 unsigned int Space = 0, bool IsDot = false) const override {
219 ConstraintSMTType Constraints = State->get<ConstraintSMT>();
220
221 Indent(Out, Space, IsDot) << "\"constraints\": ";
222 if (Constraints.isEmpty()) {
223 Out << "null," << NL;
224 return;
225 }
226
227 ++Space;
228 Out << '[' << NL;
229 for (ConstraintSMTType::iterator I = Constraints.begin();
230 I != Constraints.end(); ++I) {
231 Indent(Out, Space, IsDot)
232 << "{ \"symbol\": \"" << I->first << "\", \"range\": \"";
233 I->second->print(Out);
234 Out << "\" }";
235
236 if (std::next(I) != Constraints.end())
237 Out << ',';
238 Out << NL;
239 }
240
241 --Space;
242 Indent(Out, Space, IsDot) << "],";
243 }
244
245 bool haveEqualConstraints(ProgramStateRef S1,
246 ProgramStateRef S2) const override {
247 return S1->get<ConstraintSMT>() == S2->get<ConstraintSMT>();
248 }
249
250 bool canReasonAbout(SVal X) const override {
251 const TargetInfo &TI = getBasicVals().getContext().getTargetInfo();
252
253 std::optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>();
254 if (!SymVal)
255 return true;
256
257 const SymExpr *Sym = SymVal->getSymbol();
258 QualType Ty = Sym->getType();
259
260 // Complex types are not modeled
261 if (Ty->isComplexType() || Ty->isComplexIntegerType())
262 return false;
263
264 // Non-IEEE 754 floating-point types are not modeled
265 if ((Ty->isSpecificBuiltinType(BuiltinType::LongDouble) &&
266 (&TI.getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended() ||
267 &TI.getLongDoubleFormat() == &llvm::APFloat::PPCDoubleDouble())))
268 return false;
269
270 if (Ty->isRealFloatingType())
271 return Solver->isFPSupported();
272
273 if (isa<SymbolData>(Sym))
274 return true;
275
276 SValBuilder &SVB = getSValBuilder();
277
278 if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym))
279 return canReasonAbout(SVB.makeSymbolVal(SC->getOperand()));
280
281 if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
282 if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE))
283 return canReasonAbout(SVB.makeSymbolVal(SIE->getLHS()));
284
285 if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE))
286 return canReasonAbout(SVB.makeSymbolVal(ISE->getRHS()));
287
288 if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(BSE))
289 return canReasonAbout(SVB.makeSymbolVal(SSE->getLHS())) &&
290 canReasonAbout(SVB.makeSymbolVal(SSE->getRHS()));
291 }
292
293 llvm_unreachable("Unsupported expression to reason about!");
294 }
295
296 /// Dumps SMT formula
297 LLVM_DUMP_METHOD void dump() const { Solver->dump(); }
298
299 protected:
300 // Check whether a new model is satisfiable, and update the program state.
301 virtual ProgramStateRef assumeExpr(ProgramStateRef State, SymbolRef Sym,
302 const llvm::SMTExprRef &Exp) {
303 // Check the model, avoid simplifying AST to save time
304 if (checkModel(State, Sym, Exp).isConstrainedTrue())
305 return State->add<ConstraintSMT>(std::make_pair(Sym, Exp));
306
307 return nullptr;
308 }
309
310 /// Given a program state, construct the logical conjunction and add it to
311 /// the solver
312 virtual void addStateConstraints(ProgramStateRef State) const {
313 // TODO: Don't add all the constraints, only the relevant ones
314 auto CZ = State->get<ConstraintSMT>();
315 auto I = CZ.begin(), IE = CZ.end();
316
317 // Construct the logical AND of all the constraints
318 if (I != IE) {
319 std::vector<llvm::SMTExprRef> ASTs;
320
321 llvm::SMTExprRef Constraint = I++->second;
322 while (I != IE) {
323 Constraint = Solver->mkAnd(Constraint, I++->second);
324 }
325
326 Solver->addConstraint(Constraint);
327 }
328 }
329
330 // Generate and check a Z3 model, using the given constraint.
331 ConditionTruthVal checkModel(ProgramStateRef State, SymbolRef Sym,
332 const llvm::SMTExprRef &Exp) const {
333 ProgramStateRef NewState =
334 State->add<ConstraintSMT>(std::make_pair(Sym, Exp));
335
336 llvm::FoldingSetNodeID ID;
337 NewState->get<ConstraintSMT>().Profile(ID);
338
339 unsigned hash = ID.ComputeHash();
340 auto I = Cached.find(hash);
341 if (I != Cached.end())
342 return I->second;
343
344 Solver->reset();
345 addStateConstraints(NewState);
346
347 std::optional<bool> res = Solver->check();
348 if (!res)
349 Cached[hash] = ConditionTruthVal();
350 else
351 Cached[hash] = ConditionTruthVal(*res);
352
353 return Cached[hash];
354 }
355
356 // Cache the result of an SMT query (true, false, unknown). The key is the
357 // hash of the constraints in a state
358 mutable llvm::DenseMap<unsigned, ConditionTruthVal> Cached;
359 }; // end class SMTConstraintManager
360
361 } // namespace ento
362 } // namespace clang
363
364 #endif
365