1 | //===-- SVals.cpp - Abstract RValues for Path-Sens. Value Tracking --------===// |
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 SVal, Loc, and NonLoc, classes that represent |
10 | // abstract r-values for use with path-sensitive value tracking. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" |
15 | #include "clang/AST/ASTContext.h" |
16 | #include "clang/AST/Decl.h" |
17 | #include "clang/AST/DeclCXX.h" |
18 | #include "clang/AST/Expr.h" |
19 | #include "clang/AST/Type.h" |
20 | #include "clang/Basic/JsonSupport.h" |
21 | #include "clang/Basic/LLVM.h" |
22 | #include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" |
23 | #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" |
24 | #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" |
25 | #include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" |
26 | #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" |
27 | #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" |
28 | #include "llvm/Support/Casting.h" |
29 | #include "llvm/Support/Compiler.h" |
30 | #include "llvm/Support/ErrorHandling.h" |
31 | #include "llvm/Support/raw_ostream.h" |
32 | #include <cassert> |
33 | #include <optional> |
34 | |
35 | using namespace clang; |
36 | using namespace ento; |
37 | |
38 | //===----------------------------------------------------------------------===// |
39 | // Symbol iteration within an SVal. |
40 | //===----------------------------------------------------------------------===// |
41 | |
42 | //===----------------------------------------------------------------------===// |
43 | // Utility methods. |
44 | //===----------------------------------------------------------------------===// |
45 | |
46 | const FunctionDecl *SVal::getAsFunctionDecl() const { |
47 | if (std::optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) { |
48 | const MemRegion* R = X->getRegion(); |
49 | if (const FunctionCodeRegion *CTR = R->getAs<FunctionCodeRegion>()) |
50 | if (const auto *FD = dyn_cast<FunctionDecl>(Val: CTR->getDecl())) |
51 | return FD; |
52 | } |
53 | |
54 | if (auto X = getAs<nonloc::PointerToMember>()) { |
55 | if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Val: X->getDecl())) |
56 | return MD; |
57 | } |
58 | return nullptr; |
59 | } |
60 | |
61 | /// If this SVal is a location (subclasses Loc) and wraps a symbol, |
62 | /// return that SymbolRef. Otherwise return 0. |
63 | /// |
64 | /// Implicit casts (ex: void* -> char*) can turn Symbolic region into Element |
65 | /// region. If that is the case, gets the underlining region. |
66 | /// When IncludeBaseRegions is set to true and the SubRegion is non-symbolic, |
67 | /// the first symbolic parent region is returned. |
68 | SymbolRef SVal::getAsLocSymbol(bool IncludeBaseRegions) const { |
69 | // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? |
70 | if (const MemRegion *R = getAsRegion()) |
71 | if (const SymbolicRegion *SymR = |
72 | IncludeBaseRegions ? R->getSymbolicBase() |
73 | : dyn_cast<SymbolicRegion>(Val: R->StripCasts())) |
74 | return SymR->getSymbol(); |
75 | |
76 | return nullptr; |
77 | } |
78 | |
79 | /// Get the symbol in the SVal or its base region. |
80 | SymbolRef SVal::getLocSymbolInBase() const { |
81 | std::optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>(); |
82 | |
83 | if (!X) |
84 | return nullptr; |
85 | |
86 | const MemRegion *R = X->getRegion(); |
87 | |
88 | while (const auto *SR = dyn_cast<SubRegion>(Val: R)) { |
89 | if (const auto *SymR = dyn_cast<SymbolicRegion>(Val: SR)) |
90 | return SymR->getSymbol(); |
91 | else |
92 | R = SR->getSuperRegion(); |
93 | } |
94 | |
95 | return nullptr; |
96 | } |
97 | |
98 | /// If this SVal wraps a symbol return that SymbolRef. |
99 | /// Otherwise, return 0. |
100 | /// |
101 | /// Casts are ignored during lookup. |
102 | /// \param IncludeBaseRegions The boolean that controls whether the search |
103 | /// should continue to the base regions if the region is not symbolic. |
104 | SymbolRef SVal::getAsSymbol(bool IncludeBaseRegions) const { |
105 | // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? |
106 | if (std::optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>()) |
107 | return X->getSymbol(); |
108 | |
109 | return getAsLocSymbol(IncludeBaseRegions); |
110 | } |
111 | |
112 | const llvm::APSInt *SVal::getAsInteger() const { |
113 | if (auto CI = getAs<nonloc::ConcreteInt>()) |
114 | return &CI->getValue(); |
115 | if (auto CI = getAs<loc::ConcreteInt>()) |
116 | return &CI->getValue(); |
117 | return nullptr; |
118 | } |
119 | |
120 | const MemRegion *SVal::getAsRegion() const { |
121 | if (std::optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) |
122 | return X->getRegion(); |
123 | |
124 | if (std::optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>()) |
125 | return X->getLoc().getAsRegion(); |
126 | |
127 | return nullptr; |
128 | } |
129 | |
130 | namespace { |
131 | class TypeRetrievingVisitor |
132 | : public FullSValVisitor<TypeRetrievingVisitor, QualType> { |
133 | private: |
134 | const ASTContext &Context; |
135 | |
136 | public: |
137 | TypeRetrievingVisitor(const ASTContext &Context) : Context(Context) {} |
138 | |
139 | QualType VisitMemRegionVal(loc::MemRegionVal MRV) { |
140 | return Visit(R: MRV.getRegion()); |
141 | } |
142 | QualType VisitGotoLabel(loc::GotoLabel GL) { |
143 | return QualType{Context.VoidPtrTy}; |
144 | } |
145 | template <class ConcreteInt> QualType VisitConcreteInt(ConcreteInt CI) { |
146 | const llvm::APSInt &Value = CI.getValue(); |
147 | if (1 == Value.getBitWidth()) |
148 | return Context.BoolTy; |
149 | return Context.getIntTypeForBitwidth(DestWidth: Value.getBitWidth(), Signed: Value.isSigned()); |
150 | } |
151 | QualType VisitLocAsInteger(nonloc::LocAsInteger LI) { |
152 | QualType NestedType = Visit(V: LI.getLoc()); |
153 | if (NestedType.isNull()) |
154 | return NestedType; |
155 | |
156 | return Context.getIntTypeForBitwidth(DestWidth: LI.getNumBits(), |
157 | Signed: NestedType->isSignedIntegerType()); |
158 | } |
159 | QualType VisitCompoundVal(nonloc::CompoundVal CV) { |
160 | return CV.getValue()->getType(); |
161 | } |
162 | QualType VisitLazyCompoundVal(nonloc::LazyCompoundVal LCV) { |
163 | return LCV.getRegion()->getValueType(); |
164 | } |
165 | QualType VisitSymbolVal(nonloc::SymbolVal SV) { |
166 | return Visit(S: SV.getSymbol()); |
167 | } |
168 | QualType VisitSymbolicRegion(const SymbolicRegion *SR) { |
169 | return Visit(S: SR->getSymbol()); |
170 | } |
171 | QualType VisitAllocaRegion(const AllocaRegion *) { |
172 | return QualType{Context.VoidPtrTy}; |
173 | } |
174 | QualType VisitTypedRegion(const TypedRegion *TR) { |
175 | return TR->getLocationType(); |
176 | } |
177 | QualType VisitSymExpr(const SymExpr *SE) { return SE->getType(); } |
178 | }; |
179 | } // end anonymous namespace |
180 | |
181 | QualType SVal::getType(const ASTContext &Context) const { |
182 | TypeRetrievingVisitor TRV{Context}; |
183 | return TRV.Visit(V: *this); |
184 | } |
185 | |
186 | const MemRegion *loc::MemRegionVal::stripCasts(bool StripBaseCasts) const { |
187 | return getRegion()->StripCasts(StripBaseAndDerivedCasts: StripBaseCasts); |
188 | } |
189 | |
190 | const void *nonloc::LazyCompoundVal::getStore() const { |
191 | return static_cast<const LazyCompoundValData*>(Data)->getStore(); |
192 | } |
193 | |
194 | const TypedValueRegion *nonloc::LazyCompoundVal::getRegion() const { |
195 | return static_cast<const LazyCompoundValData*>(Data)->getRegion(); |
196 | } |
197 | |
198 | bool nonloc::PointerToMember::isNullMemberPointer() const { |
199 | return getPTMData().isNull(); |
200 | } |
201 | |
202 | const NamedDecl *nonloc::PointerToMember::getDecl() const { |
203 | const auto PTMD = this->getPTMData(); |
204 | if (PTMD.isNull()) |
205 | return nullptr; |
206 | |
207 | const NamedDecl *ND = nullptr; |
208 | if (PTMD.is<const NamedDecl *>()) |
209 | ND = PTMD.get<const NamedDecl *>(); |
210 | else |
211 | ND = PTMD.get<const PointerToMemberData *>()->getDeclaratorDecl(); |
212 | |
213 | return ND; |
214 | } |
215 | |
216 | //===----------------------------------------------------------------------===// |
217 | // Other Iterators. |
218 | //===----------------------------------------------------------------------===// |
219 | |
220 | nonloc::CompoundVal::iterator nonloc::CompoundVal::begin() const { |
221 | return getValue()->begin(); |
222 | } |
223 | |
224 | nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const { |
225 | return getValue()->end(); |
226 | } |
227 | |
228 | nonloc::PointerToMember::iterator nonloc::PointerToMember::begin() const { |
229 | const PTMDataType PTMD = getPTMData(); |
230 | if (PTMD.is<const NamedDecl *>()) |
231 | return {}; |
232 | return PTMD.get<const PointerToMemberData *>()->begin(); |
233 | } |
234 | |
235 | nonloc::PointerToMember::iterator nonloc::PointerToMember::end() const { |
236 | const PTMDataType PTMD = getPTMData(); |
237 | if (PTMD.is<const NamedDecl *>()) |
238 | return {}; |
239 | return PTMD.get<const PointerToMemberData *>()->end(); |
240 | } |
241 | |
242 | //===----------------------------------------------------------------------===// |
243 | // Useful predicates. |
244 | //===----------------------------------------------------------------------===// |
245 | |
246 | bool SVal::isConstant() const { |
247 | return getAs<nonloc::ConcreteInt>() || getAs<loc::ConcreteInt>(); |
248 | } |
249 | |
250 | bool SVal::isConstant(int I) const { |
251 | if (std::optional<loc::ConcreteInt> LV = getAs<loc::ConcreteInt>()) |
252 | return LV->getValue() == I; |
253 | if (std::optional<nonloc::ConcreteInt> NV = getAs<nonloc::ConcreteInt>()) |
254 | return NV->getValue() == I; |
255 | return false; |
256 | } |
257 | |
258 | bool SVal::isZeroConstant() const { |
259 | return isConstant(I: 0); |
260 | } |
261 | |
262 | //===----------------------------------------------------------------------===// |
263 | // Pretty-Printing. |
264 | //===----------------------------------------------------------------------===// |
265 | |
266 | LLVM_DUMP_METHOD void SVal::dump() const { dumpToStream(OS&: llvm::errs()); } |
267 | |
268 | void SVal::printJson(raw_ostream &Out, bool AddQuotes) const { |
269 | std::string Buf; |
270 | llvm::raw_string_ostream TempOut(Buf); |
271 | |
272 | dumpToStream(OS&: TempOut); |
273 | |
274 | Out << JsonFormat(RawSR: TempOut.str(), AddQuotes); |
275 | } |
276 | |
277 | void SVal::dumpToStream(raw_ostream &os) const { |
278 | if (isUndef()) { |
279 | os << "Undefined" ; |
280 | return; |
281 | } |
282 | if (isUnknown()) { |
283 | os << "Unknown" ; |
284 | return; |
285 | } |
286 | if (NonLoc::classof(V: *this)) { |
287 | castAs<NonLoc>().dumpToStream(Out&: os); |
288 | return; |
289 | } |
290 | if (Loc::classof(V: *this)) { |
291 | castAs<Loc>().dumpToStream(Out&: os); |
292 | return; |
293 | } |
294 | llvm_unreachable("Unhandled SVal kind!" ); |
295 | } |
296 | |
297 | void NonLoc::dumpToStream(raw_ostream &os) const { |
298 | switch (getKind()) { |
299 | case nonloc::ConcreteIntKind: { |
300 | const auto &Value = castAs<nonloc::ConcreteInt>().getValue(); |
301 | os << Value << ' ' << (Value.isSigned() ? 'S' : 'U') << Value.getBitWidth() |
302 | << 'b'; |
303 | break; |
304 | } |
305 | case nonloc::SymbolValKind: |
306 | os << castAs<nonloc::SymbolVal>().getSymbol(); |
307 | break; |
308 | |
309 | case nonloc::LocAsIntegerKind: { |
310 | const nonloc::LocAsInteger& C = castAs<nonloc::LocAsInteger>(); |
311 | os << C.getLoc() << " [as " << C.getNumBits() << " bit integer]" ; |
312 | break; |
313 | } |
314 | case nonloc::CompoundValKind: { |
315 | const nonloc::CompoundVal& C = castAs<nonloc::CompoundVal>(); |
316 | os << "compoundVal{" ; |
317 | bool first = true; |
318 | for (const auto &I : C) { |
319 | if (first) { |
320 | os << ' '; first = false; |
321 | } |
322 | else |
323 | os << ", " ; |
324 | |
325 | I.dumpToStream(os); |
326 | } |
327 | os << "}" ; |
328 | break; |
329 | } |
330 | case nonloc::LazyCompoundValKind: { |
331 | const nonloc::LazyCompoundVal &C = castAs<nonloc::LazyCompoundVal>(); |
332 | os << "lazyCompoundVal{" << const_cast<void *>(C.getStore()) |
333 | << ',' << C.getRegion() |
334 | << '}'; |
335 | break; |
336 | } |
337 | case nonloc::PointerToMemberKind: { |
338 | os << "pointerToMember{" ; |
339 | const nonloc::PointerToMember &CastRes = |
340 | castAs<nonloc::PointerToMember>(); |
341 | if (CastRes.getDecl()) |
342 | os << "|" << CastRes.getDecl()->getQualifiedNameAsString() << "|" ; |
343 | bool first = true; |
344 | for (const auto &I : CastRes) { |
345 | if (first) { |
346 | os << ' '; first = false; |
347 | } |
348 | else |
349 | os << ", " ; |
350 | |
351 | os << I->getType(); |
352 | } |
353 | |
354 | os << '}'; |
355 | break; |
356 | } |
357 | default: |
358 | assert(false && "Pretty-printed not implemented for this NonLoc." ); |
359 | break; |
360 | } |
361 | } |
362 | |
363 | void Loc::dumpToStream(raw_ostream &os) const { |
364 | switch (getKind()) { |
365 | case loc::ConcreteIntKind: |
366 | os << castAs<loc::ConcreteInt>().getValue().getZExtValue() << " (Loc)" ; |
367 | break; |
368 | case loc::GotoLabelKind: |
369 | os << "&&" << castAs<loc::GotoLabel>().getLabel()->getName(); |
370 | break; |
371 | case loc::MemRegionValKind: |
372 | os << '&' << castAs<loc::MemRegionVal>().getRegion()->getString(); |
373 | break; |
374 | default: |
375 | llvm_unreachable("Pretty-printing not implemented for this Loc." ); |
376 | } |
377 | } |
378 | |