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