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