1 //==- NonnullGlobalConstantsChecker.cpp ---------------------------*- C++ -*--// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This checker adds an assumption that constant globals of certain types* are 11 // non-null, as otherwise they generally do not convey any useful information. 12 // The assumption is useful, as many framework use e. g. global const strings, 13 // and the analyzer might not be able to infer the global value if the 14 // definition is in a separate translation unit. 15 // The following types (and their typedef aliases) are considered to be 16 // non-null: 17 // - `char* const` 18 // - `const CFStringRef` from CoreFoundation 19 // - `NSString* const` from Foundation 20 // - `CFBooleanRef` from Foundation 21 // 22 //===----------------------------------------------------------------------===// 23 24 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 25 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 26 #include "clang/StaticAnalyzer/Core/Checker.h" 27 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 28 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 29 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 30 31 using namespace clang; 32 using namespace ento; 33 34 namespace { 35 36 class NonnullGlobalConstantsChecker : public Checker<check::Location> { 37 mutable IdentifierInfo *NSStringII = nullptr; 38 mutable IdentifierInfo *CFStringRefII = nullptr; 39 mutable IdentifierInfo *CFBooleanRefII = nullptr; 40 41 public: 42 NonnullGlobalConstantsChecker() {} 43 44 void checkLocation(SVal l, bool isLoad, const Stmt *S, 45 CheckerContext &C) const; 46 47 private: 48 void initIdentifierInfo(ASTContext &Ctx) const; 49 50 bool isGlobalConstString(SVal V) const; 51 52 bool isNonnullType(QualType Ty) const; 53 }; 54 55 } // namespace 56 57 /// Lazily initialize cache for required identifier information. 58 void NonnullGlobalConstantsChecker::initIdentifierInfo(ASTContext &Ctx) const { 59 if (NSStringII) 60 return; 61 62 NSStringII = &Ctx.Idents.get("NSString"); 63 CFStringRefII = &Ctx.Idents.get("CFStringRef"); 64 CFBooleanRefII = &Ctx.Idents.get("CFBooleanRef"); 65 } 66 67 /// Add an assumption that const string-like globals are non-null. 68 void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad, 69 const Stmt *S, 70 CheckerContext &C) const { 71 initIdentifierInfo(C.getASTContext()); 72 if (!isLoad || !location.isValid()) 73 return; 74 75 ProgramStateRef State = C.getState(); 76 77 if (isGlobalConstString(location)) { 78 SVal V = State->getSVal(location.castAs<Loc>()); 79 Optional<DefinedOrUnknownSVal> Constr = V.getAs<DefinedOrUnknownSVal>(); 80 81 if (Constr) { 82 83 // Assume that the variable is non-null. 84 ProgramStateRef OutputState = State->assume(*Constr, true); 85 C.addTransition(OutputState); 86 } 87 } 88 } 89 90 /// \param V loaded lvalue. 91 /// \return whether {@code val} is a string-like const global. 92 bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const { 93 Optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>(); 94 if (!RegionVal) 95 return false; 96 auto *Region = dyn_cast<VarRegion>(RegionVal->getAsRegion()); 97 if (!Region) 98 return false; 99 const VarDecl *Decl = Region->getDecl(); 100 101 if (!Decl->hasGlobalStorage()) 102 return false; 103 104 QualType Ty = Decl->getType(); 105 bool HasConst = Ty.isConstQualified(); 106 if (isNonnullType(Ty) && HasConst) 107 return true; 108 109 // Look through the typedefs. 110 while (auto *T = dyn_cast<TypedefType>(Ty)) { 111 Ty = T->getDecl()->getUnderlyingType(); 112 113 // It is sufficient for any intermediate typedef 114 // to be classified const. 115 HasConst = HasConst || Ty.isConstQualified(); 116 if (isNonnullType(Ty) && HasConst) 117 return true; 118 } 119 return false; 120 } 121 122 /// \return whether {@code type} is extremely unlikely to be null 123 bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const { 124 125 if (Ty->isPointerType() && Ty->getPointeeType()->isCharType()) 126 return true; 127 128 if (auto *T = dyn_cast<ObjCObjectPointerType>(Ty)) { 129 return T->getInterfaceDecl() && 130 T->getInterfaceDecl()->getIdentifier() == NSStringII; 131 } else if (auto *T = dyn_cast<TypedefType>(Ty)) { 132 IdentifierInfo* II = T->getDecl()->getIdentifier(); 133 return II == CFStringRefII || II == CFBooleanRefII; 134 } 135 return false; 136 } 137 138 void ento::registerNonnullGlobalConstantsChecker(CheckerManager &Mgr) { 139 Mgr.registerChecker<NonnullGlobalConstantsChecker>(); 140 } 141