1 | //===- SVals.h - Abstract Values for Static Analysis ------------*- 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 SVal, Loc, and NonLoc, classes that represent |
10 | // abstract r-values for use with path-sensitive value tracking. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALS_H |
15 | #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALS_H |
16 | |
17 | #include "clang/AST/Expr.h" |
18 | #include "clang/AST/Type.h" |
19 | #include "clang/Basic/LLVM.h" |
20 | #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" |
21 | #include "llvm/ADT/APSInt.h" |
22 | #include "llvm/ADT/FoldingSet.h" |
23 | #include "llvm/ADT/ImmutableList.h" |
24 | #include "llvm/ADT/PointerUnion.h" |
25 | #include "llvm/ADT/STLForwardCompat.h" |
26 | #include "llvm/ADT/iterator_range.h" |
27 | #include "llvm/Support/Casting.h" |
28 | #include <cassert> |
29 | #include <cstdint> |
30 | #include <optional> |
31 | #include <utility> |
32 | |
33 | //==------------------------------------------------------------------------==// |
34 | // Base SVal types. |
35 | //==------------------------------------------------------------------------==// |
36 | |
37 | namespace clang { |
38 | |
39 | class CXXBaseSpecifier; |
40 | class FunctionDecl; |
41 | class LabelDecl; |
42 | |
43 | namespace ento { |
44 | |
45 | class CompoundValData; |
46 | class LazyCompoundValData; |
47 | class MemRegion; |
48 | class PointerToMemberData; |
49 | class SValBuilder; |
50 | class TypedValueRegion; |
51 | |
52 | /// SVal - This represents a symbolic expression, which can be either |
53 | /// an L-value or an R-value. |
54 | /// |
55 | class SVal { |
56 | public: |
57 | enum SValKind : unsigned char { |
58 | #define BASIC_SVAL(Id, Parent) Id##Kind, |
59 | #define LOC_SVAL(Id, Parent) Loc##Id##Kind, |
60 | #define NONLOC_SVAL(Id, Parent) NonLoc##Id##Kind, |
61 | #define SVAL_RANGE(Id, First, Last) \ |
62 | BEGIN_##Id = Id##First##Kind, END_##Id = Id##Last##Kind, |
63 | #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" |
64 | }; |
65 | |
66 | protected: |
67 | const void *Data = nullptr; |
68 | SValKind Kind = UndefinedValKind; |
69 | |
70 | explicit SVal(SValKind Kind, const void *Data = nullptr) |
71 | : Data(Data), Kind(Kind) {} |
72 | |
73 | template <typename T> const T *castDataAs() const { |
74 | return static_cast<const T *>(Data); |
75 | } |
76 | |
77 | public: |
78 | explicit SVal() = default; |
79 | |
80 | /// Convert to the specified SVal type, asserting that this SVal is of |
81 | /// the desired type. |
82 | template <typename T> T castAs() const { return llvm::cast<T>(*this); } |
83 | |
84 | /// Convert to the specified SVal type, returning std::nullopt if this SVal is |
85 | /// not of the desired type. |
86 | template <typename T> std::optional<T> getAs() const { |
87 | return llvm::dyn_cast<T>(*this); |
88 | } |
89 | |
90 | SValKind getKind() const { return Kind; } |
91 | |
92 | // This method is required for using SVal in a FoldingSetNode. It |
93 | // extracts a unique signature for this SVal object. |
94 | void Profile(llvm::FoldingSetNodeID &ID) const { |
95 | ID.AddPointer(Ptr: Data); |
96 | ID.AddInteger(I: llvm::to_underlying(E: getKind())); |
97 | } |
98 | |
99 | bool operator==(SVal R) const { return Kind == R.Kind && Data == R.Data; } |
100 | bool operator!=(SVal R) const { return !(*this == R); } |
101 | |
102 | bool isUnknown() const { return getKind() == UnknownValKind; } |
103 | |
104 | bool isUndef() const { return getKind() == UndefinedValKind; } |
105 | |
106 | bool isUnknownOrUndef() const { return isUnknown() || isUndef(); } |
107 | |
108 | bool isValid() const { return !isUnknownOrUndef(); } |
109 | |
110 | bool isConstant() const; |
111 | |
112 | bool isConstant(int I) const; |
113 | |
114 | bool isZeroConstant() const; |
115 | |
116 | /// getAsFunctionDecl - If this SVal is a MemRegionVal and wraps a |
117 | /// CodeTextRegion wrapping a FunctionDecl, return that FunctionDecl. |
118 | /// Otherwise return 0. |
119 | const FunctionDecl *getAsFunctionDecl() const; |
120 | |
121 | /// If this SVal is a location and wraps a symbol, return that |
122 | /// SymbolRef. Otherwise return 0. |
123 | /// |
124 | /// Casts are ignored during lookup. |
125 | /// \param IncludeBaseRegions The boolean that controls whether the search |
126 | /// should continue to the base regions if the region is not symbolic. |
127 | SymbolRef getAsLocSymbol(bool IncludeBaseRegions = false) const; |
128 | |
129 | /// Get the symbol in the SVal or its base region. |
130 | SymbolRef getLocSymbolInBase() const; |
131 | |
132 | /// If this SVal wraps a symbol return that SymbolRef. |
133 | /// Otherwise, return 0. |
134 | /// |
135 | /// Casts are ignored during lookup. |
136 | /// \param IncludeBaseRegions The boolean that controls whether the search |
137 | /// should continue to the base regions if the region is not symbolic. |
138 | SymbolRef getAsSymbol(bool IncludeBaseRegions = false) const; |
139 | |
140 | /// If this SVal is loc::ConcreteInt or nonloc::ConcreteInt, |
141 | /// return a pointer to APSInt which is held in it. |
142 | /// Otherwise, return nullptr. |
143 | const llvm::APSInt *getAsInteger() const; |
144 | |
145 | const MemRegion *getAsRegion() const; |
146 | |
147 | /// printJson - Pretty-prints in JSON format. |
148 | void printJson(raw_ostream &Out, bool AddQuotes) const; |
149 | |
150 | void dumpToStream(raw_ostream &OS) const; |
151 | void dump() const; |
152 | |
153 | llvm::iterator_range<SymExpr::symbol_iterator> symbols() const { |
154 | if (const SymExpr *SE = getAsSymbol(/*IncludeBaseRegions=*/IncludeBaseRegions: true)) |
155 | return SE->symbols(); |
156 | SymExpr::symbol_iterator end{}; |
157 | return llvm::make_range(x: end, y: end); |
158 | } |
159 | |
160 | /// Try to get a reasonable type for the given value. |
161 | /// |
162 | /// \returns The best approximation of the value type or Null. |
163 | /// In theory, all symbolic values should be typed, but this function |
164 | /// is still a WIP and might have a few blind spots. |
165 | /// |
166 | /// \note This function should not be used when the user has access to the |
167 | /// bound expression AST node as well, since AST always has exact types. |
168 | /// |
169 | /// \note Loc values are interpreted as pointer rvalues for the purposes of |
170 | /// this method. |
171 | QualType getType(const ASTContext &) const; |
172 | }; |
173 | |
174 | inline raw_ostream &operator<<(raw_ostream &os, clang::ento::SVal V) { |
175 | V.dumpToStream(OS&: os); |
176 | return os; |
177 | } |
178 | |
179 | namespace nonloc { |
180 | /// Sub-kinds for NonLoc values. |
181 | #define NONLOC_SVAL(Id, Parent) \ |
182 | inline constexpr auto Id##Kind = SVal::SValKind::NonLoc##Id##Kind; |
183 | #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" |
184 | } // namespace nonloc |
185 | |
186 | namespace loc { |
187 | /// Sub-kinds for Loc values. |
188 | #define LOC_SVAL(Id, Parent) \ |
189 | inline constexpr auto Id##Kind = SVal::SValKind::Loc##Id##Kind; |
190 | #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" |
191 | } // namespace loc |
192 | |
193 | class UndefinedVal : public SVal { |
194 | public: |
195 | UndefinedVal() : SVal(UndefinedValKind) {} |
196 | static bool classof(SVal V) { return V.getKind() == UndefinedValKind; } |
197 | }; |
198 | |
199 | class DefinedOrUnknownSVal : public SVal { |
200 | public: |
201 | // We want calling these methods to be a compiler error since they are |
202 | // tautologically false. |
203 | bool isUndef() const = delete; |
204 | bool isValid() const = delete; |
205 | |
206 | static bool classof(SVal V) { return !V.isUndef(); } |
207 | |
208 | protected: |
209 | explicit DefinedOrUnknownSVal(SValKind Kind, const void *Data = nullptr) |
210 | : SVal(Kind, Data) {} |
211 | }; |
212 | |
213 | class UnknownVal : public DefinedOrUnknownSVal { |
214 | public: |
215 | explicit UnknownVal() : DefinedOrUnknownSVal(UnknownValKind) {} |
216 | |
217 | static bool classof(SVal V) { return V.getKind() == UnknownValKind; } |
218 | }; |
219 | |
220 | class DefinedSVal : public DefinedOrUnknownSVal { |
221 | public: |
222 | // We want calling these methods to be a compiler error since they are |
223 | // tautologically true/false. |
224 | bool isUnknown() const = delete; |
225 | bool isUnknownOrUndef() const = delete; |
226 | bool isValid() const = delete; |
227 | |
228 | static bool classof(SVal V) { return !V.isUnknownOrUndef(); } |
229 | |
230 | protected: |
231 | explicit DefinedSVal(SValKind Kind, const void *Data) |
232 | : DefinedOrUnknownSVal(Kind, Data) {} |
233 | }; |
234 | |
235 | class NonLoc : public DefinedSVal { |
236 | protected: |
237 | NonLoc(SValKind Kind, const void *Data) : DefinedSVal(Kind, Data) {} |
238 | |
239 | public: |
240 | void dumpToStream(raw_ostream &Out) const; |
241 | |
242 | static bool isCompoundType(QualType T) { |
243 | return T->isArrayType() || T->isRecordType() || |
244 | T->isAnyComplexType() || T->isVectorType(); |
245 | } |
246 | |
247 | static bool classof(SVal V) { |
248 | return BEGIN_NonLoc <= V.getKind() && V.getKind() <= END_NonLoc; |
249 | } |
250 | }; |
251 | |
252 | class Loc : public DefinedSVal { |
253 | protected: |
254 | Loc(SValKind Kind, const void *Data) : DefinedSVal(Kind, Data) {} |
255 | |
256 | public: |
257 | void dumpToStream(raw_ostream &Out) const; |
258 | |
259 | static bool isLocType(QualType T) { |
260 | return T->isAnyPointerType() || T->isBlockPointerType() || |
261 | T->isReferenceType() || T->isNullPtrType(); |
262 | } |
263 | |
264 | static bool classof(SVal V) { |
265 | return BEGIN_Loc <= V.getKind() && V.getKind() <= END_Loc; |
266 | } |
267 | }; |
268 | |
269 | //==------------------------------------------------------------------------==// |
270 | // Subclasses of NonLoc. |
271 | //==------------------------------------------------------------------------==// |
272 | |
273 | namespace nonloc { |
274 | |
275 | /// Represents symbolic expression that isn't a location. |
276 | class SymbolVal : public NonLoc { |
277 | public: |
278 | SymbolVal() = delete; |
279 | explicit SymbolVal(SymbolRef Sym) : NonLoc(SymbolValKind, Sym) { |
280 | assert(Sym); |
281 | assert(!Loc::isLocType(Sym->getType())); |
282 | } |
283 | |
284 | LLVM_ATTRIBUTE_RETURNS_NONNULL |
285 | SymbolRef getSymbol() const { |
286 | return (const SymExpr *) Data; |
287 | } |
288 | |
289 | bool isExpression() const { |
290 | return !isa<SymbolData>(Val: getSymbol()); |
291 | } |
292 | |
293 | static bool classof(SVal V) { return V.getKind() == SymbolValKind; } |
294 | }; |
295 | |
296 | /// Value representing integer constant. |
297 | class ConcreteInt : public NonLoc { |
298 | public: |
299 | explicit ConcreteInt(const llvm::APSInt &V) : NonLoc(ConcreteIntKind, &V) {} |
300 | |
301 | const llvm::APSInt &getValue() const { return *castDataAs<llvm::APSInt>(); } |
302 | |
303 | static bool classof(SVal V) { return V.getKind() == ConcreteIntKind; } |
304 | }; |
305 | |
306 | class LocAsInteger : public NonLoc { |
307 | friend class ento::SValBuilder; |
308 | |
309 | explicit LocAsInteger(const std::pair<SVal, uintptr_t> &data) |
310 | : NonLoc(LocAsIntegerKind, &data) { |
311 | // We do not need to represent loc::ConcreteInt as LocAsInteger, |
312 | // as it'd collapse into a nonloc::ConcreteInt instead. |
313 | [[maybe_unused]] SValKind K = data.first.getKind(); |
314 | assert(K == loc::MemRegionValKind || K == loc::GotoLabelKind); |
315 | } |
316 | |
317 | public: |
318 | Loc getLoc() const { |
319 | return castDataAs<std::pair<SVal, uintptr_t>>()->first.castAs<Loc>(); |
320 | } |
321 | |
322 | unsigned getNumBits() const { |
323 | return castDataAs<std::pair<SVal, uintptr_t>>()->second; |
324 | } |
325 | |
326 | static bool classof(SVal V) { return V.getKind() == LocAsIntegerKind; } |
327 | }; |
328 | |
329 | class CompoundVal : public NonLoc { |
330 | friend class ento::SValBuilder; |
331 | |
332 | explicit CompoundVal(const CompoundValData *D) : NonLoc(CompoundValKind, D) { |
333 | assert(D); |
334 | } |
335 | |
336 | public: |
337 | LLVM_ATTRIBUTE_RETURNS_NONNULL |
338 | const CompoundValData* getValue() const { |
339 | return castDataAs<CompoundValData>(); |
340 | } |
341 | |
342 | using iterator = llvm::ImmutableList<SVal>::iterator; |
343 | iterator begin() const; |
344 | iterator end() const; |
345 | |
346 | static bool classof(SVal V) { return V.getKind() == CompoundValKind; } |
347 | }; |
348 | |
349 | class LazyCompoundVal : public NonLoc { |
350 | friend class ento::SValBuilder; |
351 | |
352 | explicit LazyCompoundVal(const LazyCompoundValData *D) |
353 | : NonLoc(LazyCompoundValKind, D) { |
354 | assert(D); |
355 | } |
356 | |
357 | public: |
358 | LLVM_ATTRIBUTE_RETURNS_NONNULL |
359 | const LazyCompoundValData *getCVData() const { |
360 | return castDataAs<LazyCompoundValData>(); |
361 | } |
362 | |
363 | /// It might return null. |
364 | const void *getStore() const; |
365 | |
366 | LLVM_ATTRIBUTE_RETURNS_NONNULL |
367 | const TypedValueRegion *getRegion() const; |
368 | |
369 | static bool classof(SVal V) { return V.getKind() == LazyCompoundValKind; } |
370 | }; |
371 | |
372 | /// Value representing pointer-to-member. |
373 | /// |
374 | /// This value is qualified as NonLoc because neither loading nor storing |
375 | /// operations are applied to it. Instead, the analyzer uses the L-value coming |
376 | /// from pointer-to-member applied to an object. |
377 | /// This SVal is represented by a NamedDecl which can be a member function |
378 | /// pointer or a member data pointer and an optional list of CXXBaseSpecifiers. |
379 | /// This list is required to accumulate the pointer-to-member cast history to |
380 | /// figure out the correct subobject field. In particular, implicit casts grow |
381 | /// this list and explicit casts like static_cast shrink this list. |
382 | class PointerToMember : public NonLoc { |
383 | friend class ento::SValBuilder; |
384 | |
385 | public: |
386 | using PTMDataType = |
387 | llvm::PointerUnion<const NamedDecl *, const PointerToMemberData *>; |
388 | |
389 | const PTMDataType getPTMData() const { |
390 | return PTMDataType::getFromOpaqueValue(VP: const_cast<void *>(Data)); |
391 | } |
392 | |
393 | bool isNullMemberPointer() const; |
394 | |
395 | const NamedDecl *getDecl() const; |
396 | |
397 | template<typename AdjustedDecl> |
398 | const AdjustedDecl *getDeclAs() const { |
399 | return dyn_cast_or_null<AdjustedDecl>(getDecl()); |
400 | } |
401 | |
402 | using iterator = llvm::ImmutableList<const CXXBaseSpecifier *>::iterator; |
403 | |
404 | iterator begin() const; |
405 | iterator end() const; |
406 | |
407 | static bool classof(SVal V) { return V.getKind() == PointerToMemberKind; } |
408 | |
409 | private: |
410 | explicit PointerToMember(const PTMDataType D) |
411 | : NonLoc(PointerToMemberKind, D.getOpaqueValue()) {} |
412 | }; |
413 | |
414 | } // namespace nonloc |
415 | |
416 | //==------------------------------------------------------------------------==// |
417 | // Subclasses of Loc. |
418 | //==------------------------------------------------------------------------==// |
419 | |
420 | namespace loc { |
421 | |
422 | class GotoLabel : public Loc { |
423 | public: |
424 | explicit GotoLabel(const LabelDecl *Label) : Loc(GotoLabelKind, Label) { |
425 | assert(Label); |
426 | } |
427 | |
428 | const LabelDecl *getLabel() const { return castDataAs<LabelDecl>(); } |
429 | |
430 | static bool classof(SVal V) { return V.getKind() == GotoLabelKind; } |
431 | }; |
432 | |
433 | class MemRegionVal : public Loc { |
434 | public: |
435 | explicit MemRegionVal(const MemRegion *r) : Loc(MemRegionValKind, r) { |
436 | assert(r); |
437 | } |
438 | |
439 | /// Get the underlining region. |
440 | LLVM_ATTRIBUTE_RETURNS_NONNULL |
441 | const MemRegion *getRegion() const { return castDataAs<MemRegion>(); } |
442 | |
443 | /// Get the underlining region and strip casts. |
444 | LLVM_ATTRIBUTE_RETURNS_NONNULL |
445 | const MemRegion* stripCasts(bool StripBaseCasts = true) const; |
446 | |
447 | template <typename REGION> |
448 | const REGION* getRegionAs() const { |
449 | return dyn_cast<REGION>(getRegion()); |
450 | } |
451 | |
452 | bool operator==(const MemRegionVal &R) const { |
453 | return getRegion() == R.getRegion(); |
454 | } |
455 | |
456 | bool operator!=(const MemRegionVal &R) const { |
457 | return getRegion() != R.getRegion(); |
458 | } |
459 | |
460 | static bool classof(SVal V) { return V.getKind() == MemRegionValKind; } |
461 | }; |
462 | |
463 | class ConcreteInt : public Loc { |
464 | public: |
465 | explicit ConcreteInt(const llvm::APSInt &V) : Loc(ConcreteIntKind, &V) {} |
466 | |
467 | const llvm::APSInt &getValue() const { return *castDataAs<llvm::APSInt>(); } |
468 | |
469 | static bool classof(SVal V) { return V.getKind() == ConcreteIntKind; } |
470 | }; |
471 | |
472 | } // namespace loc |
473 | } // namespace ento |
474 | } // namespace clang |
475 | |
476 | namespace llvm { |
477 | template <typename To, typename From> |
478 | struct CastInfo< |
479 | To, From, |
480 | std::enable_if_t<std::is_base_of<::clang::ento::SVal, From>::value>> |
481 | : public CastIsPossible<To, ::clang::ento::SVal> { |
482 | using Self = CastInfo< |
483 | To, From, |
484 | std::enable_if_t<std::is_base_of<::clang::ento::SVal, From>::value>>; |
485 | static bool isPossible(const From &V) { |
486 | return To::classof(*static_cast<const ::clang::ento::SVal *>(&V)); |
487 | } |
488 | static std::optional<To> castFailed() { return std::optional<To>{}; } |
489 | static To doCast(const From &f) { |
490 | return *static_cast<const To *>(cast<::clang::ento::SVal>(&f)); |
491 | } |
492 | static std::optional<To> doCastIfPossible(const From &f) { |
493 | if (!Self::isPossible(V: f)) |
494 | return Self::castFailed(); |
495 | return doCast(f); |
496 | } |
497 | }; |
498 | } // namespace llvm |
499 | |
500 | #endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALS_H |
501 | |