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) Data(Data)71 : Data(Data), Kind(Kind) {} 72 castDataAs()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. castAs()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. getAs()86 template <typename T> std::optional<T> getAs() const { 87 return llvm::dyn_cast<T>(*this); 88 } 89 getKind()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. Profile(llvm::FoldingSetNodeID & ID)94 void Profile(llvm::FoldingSetNodeID &ID) const { 95 ID.AddPointer(Data); 96 ID.AddInteger(llvm::to_underlying(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 isUnknown()102 bool isUnknown() const { return getKind() == UnknownValKind; } 103 isUndef()104 bool isUndef() const { return getKind() == UndefinedValKind; } 105 isUnknownOrUndef()106 bool isUnknownOrUndef() const { return isUnknown() || isUndef(); } 107 isValid()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 symbols()153 llvm::iterator_range<SymExpr::symbol_iterator> symbols() const { 154 if (const SymExpr *SE = getAsSymbol(/*IncludeBaseRegions=*/true)) 155 return SE->symbols(); 156 SymExpr::symbol_iterator end{}; 157 return llvm::make_range(end, 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); 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: UndefinedVal()195 UndefinedVal() : SVal(UndefinedValKind) {} classof(SVal V)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 classof(SVal V)206 static bool classof(SVal V) { return !V.isUndef(); } 207 208 protected: 209 explicit DefinedOrUnknownSVal(SValKind Kind, const void *Data = nullptr) SVal(Kind,Data)210 : SVal(Kind, Data) {} 211 }; 212 213 class UnknownVal : public DefinedOrUnknownSVal { 214 public: UnknownVal()215 explicit UnknownVal() : DefinedOrUnknownSVal(UnknownValKind) {} 216 classof(SVal V)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 classof(SVal V)228 static bool classof(SVal V) { return !V.isUnknownOrUndef(); } 229 230 protected: DefinedSVal(SValKind Kind,const void * Data)231 explicit DefinedSVal(SValKind Kind, const void *Data) 232 : DefinedOrUnknownSVal(Kind, Data) {} 233 }; 234 235 class NonLoc : public DefinedSVal { 236 protected: NonLoc(SValKind Kind,const void * Data)237 NonLoc(SValKind Kind, const void *Data) : DefinedSVal(Kind, Data) {} 238 239 public: 240 void dumpToStream(raw_ostream &Out) const; 241 isCompoundType(QualType T)242 static bool isCompoundType(QualType T) { 243 return T->isArrayType() || T->isRecordType() || 244 T->isAnyComplexType() || T->isVectorType(); 245 } 246 classof(SVal V)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: Loc(SValKind Kind,const void * Data)254 Loc(SValKind Kind, const void *Data) : DefinedSVal(Kind, Data) {} 255 256 public: 257 void dumpToStream(raw_ostream &Out) const; 258 isLocType(QualType T)259 static bool isLocType(QualType T) { 260 return T->isAnyPointerType() || T->isBlockPointerType() || 261 T->isReferenceType() || T->isNullPtrType(); 262 } 263 classof(SVal V)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; SymbolVal(SymbolRef Sym)279 explicit SymbolVal(SymbolRef Sym) : NonLoc(SymbolValKind, Sym) { 280 assert(Sym); 281 assert(!Loc::isLocType(Sym->getType())); 282 } 283 284 LLVM_ATTRIBUTE_RETURNS_NONNULL getSymbol()285 SymbolRef getSymbol() const { 286 return (const SymExpr *) Data; 287 } 288 isExpression()289 bool isExpression() const { 290 return !isa<SymbolData>(getSymbol()); 291 } 292 classof(SVal V)293 static bool classof(SVal V) { return V.getKind() == SymbolValKind; } 294 }; 295 296 /// Value representing integer constant. 297 class ConcreteInt : public NonLoc { 298 public: ConcreteInt(const llvm::APSInt & V)299 explicit ConcreteInt(const llvm::APSInt &V) : NonLoc(ConcreteIntKind, &V) {} 300 getValue()301 const llvm::APSInt &getValue() const { return *castDataAs<llvm::APSInt>(); } 302 classof(SVal V)303 static bool classof(SVal V) { return V.getKind() == ConcreteIntKind; } 304 }; 305 306 class LocAsInteger : public NonLoc { 307 friend class ento::SValBuilder; 308 LocAsInteger(const std::pair<SVal,uintptr_t> & data)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: getLoc()318 Loc getLoc() const { 319 return castDataAs<std::pair<SVal, uintptr_t>>()->first.castAs<Loc>(); 320 } 321 getNumBits()322 unsigned getNumBits() const { 323 return castDataAs<std::pair<SVal, uintptr_t>>()->second; 324 } 325 classof(SVal V)326 static bool classof(SVal V) { return V.getKind() == LocAsIntegerKind; } 327 }; 328 329 class CompoundVal : public NonLoc { 330 friend class ento::SValBuilder; 331 CompoundVal(const CompoundValData * D)332 explicit CompoundVal(const CompoundValData *D) : NonLoc(CompoundValKind, D) { 333 assert(D); 334 } 335 336 public: 337 LLVM_ATTRIBUTE_RETURNS_NONNULL getValue()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 classof(SVal V)346 static bool classof(SVal V) { return V.getKind() == CompoundValKind; } 347 }; 348 349 class LazyCompoundVal : public NonLoc { 350 friend class ento::SValBuilder; 351 LazyCompoundVal(const LazyCompoundValData * D)352 explicit LazyCompoundVal(const LazyCompoundValData *D) 353 : NonLoc(LazyCompoundValKind, D) { 354 assert(D); 355 } 356 357 public: 358 LLVM_ATTRIBUTE_RETURNS_NONNULL getCVData()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 classof(SVal V)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 getPTMData()389 const PTMDataType getPTMData() const { 390 return PTMDataType::getFromOpaqueValue(const_cast<void *>(Data)); 391 } 392 393 bool isNullMemberPointer() const; 394 395 const NamedDecl *getDecl() const; 396 397 template<typename AdjustedDecl> getDeclAs()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 classof(SVal V)407 static bool classof(SVal V) { return V.getKind() == PointerToMemberKind; } 408 409 private: PointerToMember(const PTMDataType D)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: GotoLabel(const LabelDecl * Label)424 explicit GotoLabel(const LabelDecl *Label) : Loc(GotoLabelKind, Label) { 425 assert(Label); 426 } 427 getLabel()428 const LabelDecl *getLabel() const { return castDataAs<LabelDecl>(); } 429 classof(SVal V)430 static bool classof(SVal V) { return V.getKind() == GotoLabelKind; } 431 }; 432 433 class MemRegionVal : public Loc { 434 public: MemRegionVal(const MemRegion * r)435 explicit MemRegionVal(const MemRegion *r) : Loc(MemRegionValKind, r) { 436 assert(r); 437 } 438 439 /// Get the underlining region. 440 LLVM_ATTRIBUTE_RETURNS_NONNULL getRegion()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> getRegionAs()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 classof(SVal V)460 static bool classof(SVal V) { return V.getKind() == MemRegionValKind; } 461 }; 462 463 class ConcreteInt : public Loc { 464 public: ConcreteInt(const llvm::APSInt & V)465 explicit ConcreteInt(const llvm::APSInt &V) : Loc(ConcreteIntKind, &V) {} 466 getValue()467 const llvm::APSInt &getValue() const { return *castDataAs<llvm::APSInt>(); } 468 classof(SVal V)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(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