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 30using namespace clang; 31using namespace ento; 32 33namespace { 34 35class NonnullGlobalConstantsChecker : public Checker<check::Location> { 36 mutable IdentifierInfo *NSStringII = nullptr; 37 mutable IdentifierInfo *CFStringRefII = nullptr; 38 mutable IdentifierInfo *CFBooleanRefII = nullptr; 39 mutable IdentifierInfo *CFNullRefII = nullptr; 40 41public: 42 NonnullGlobalConstantsChecker() {} 43 44 void checkLocation(SVal l, bool isLoad, const Stmt *S, 45 CheckerContext &C) const; 46 47private: 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. 58void 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 CFNullRefII = &Ctx.Idents.get("CFNullRef"); 66} 67 68/// Add an assumption that const string-like globals are non-null. 69void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad, 70 const Stmt *S, 71 CheckerContext &C) const { 72 initIdentifierInfo(C.getASTContext()); 73 if (!isLoad || !location.isValid()) 74 return; 75 76 ProgramStateRef State = C.getState(); 77 78 if (isGlobalConstString(location)) { 79 SVal V = State->getSVal(location.castAs<Loc>()); 80 Optional<DefinedOrUnknownSVal> Constr = V.getAs<DefinedOrUnknownSVal>(); 81 82 if (Constr) { 83 84 // Assume that the variable is non-null. 85 ProgramStateRef OutputState = State->assume(*Constr, true); 86 C.addTransition(OutputState); 87 } 88 } 89} 90 91/// \param V loaded lvalue. 92/// \return whether {@code val} is a string-like const global. 93bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const { 94 Optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>(); 95 if (!RegionVal) 96 return false; 97 auto *Region = dyn_cast<VarRegion>(RegionVal->getAsRegion()); 98 if (!Region) 99 return false; 100 const VarDecl *Decl = Region->getDecl(); 101 102 if (!Decl->hasGlobalStorage()) 103 return false; 104 105 QualType Ty = Decl->getType(); 106 bool HasConst = Ty.isConstQualified(); 107 if (isNonnullType(Ty) && HasConst) 108 return true; 109 110 // Look through the typedefs. 111 while (const Type *T = Ty.getTypePtr()) { 112 if (const auto *TT = dyn_cast<TypedefType>(T)) { 113 Ty = TT->getDecl()->getUnderlyingType(); 114 // It is sufficient for any intermediate typedef 115 // to be classified const. 116 HasConst = HasConst || Ty.isConstQualified(); 117 if (isNonnullType(Ty) && HasConst) 118 return true; 119 } else if (const auto *AT = dyn_cast<AttributedType>(T)) { 120 if (AT->getAttrKind() == attr::TypeNonNull) 121 return true; 122 Ty = AT->getModifiedType(); 123 } else { 124 return false; 125 } 126 } 127 return false; 128} 129 130/// \return whether {@code type} is extremely unlikely to be null 131bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const { 132 133 if (Ty->isPointerType() && Ty->getPointeeType()->isCharType()) 134 return true; 135 136 if (auto *T = dyn_cast<ObjCObjectPointerType>(Ty)) { 137 return T->getInterfaceDecl() && 138 T->getInterfaceDecl()->getIdentifier() == NSStringII; 139 } else if (auto *T = dyn_cast<TypedefType>(Ty)) { 140 IdentifierInfo* II = T->getDecl()->getIdentifier(); 141 return II == CFStringRefII || II == CFBooleanRefII || II == CFNullRefII; 142 } 143 return false; 144} 145 146void ento::registerNonnullGlobalConstantsChecker(CheckerManager &Mgr) { 147 Mgr.registerChecker<NonnullGlobalConstantsChecker>(); 148} 149 150bool ento::shouldRegisterNonnullGlobalConstantsChecker(const LangOptions &LO) { 151 return true; 152} 153