10b57cec5SDimitry Andric //==- NonnullGlobalConstantsChecker.cpp ---------------------------*- C++ -*--// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This checker adds an assumption that constant globals of certain types* are 100b57cec5SDimitry Andric // non-null, as otherwise they generally do not convey any useful information. 110b57cec5SDimitry Andric // The assumption is useful, as many framework use e. g. global const strings, 120b57cec5SDimitry Andric // and the analyzer might not be able to infer the global value if the 130b57cec5SDimitry Andric // definition is in a separate translation unit. 140b57cec5SDimitry Andric // The following types (and their typedef aliases) are considered to be 150b57cec5SDimitry Andric // non-null: 160b57cec5SDimitry Andric // - `char* const` 170b57cec5SDimitry Andric // - `const CFStringRef` from CoreFoundation 180b57cec5SDimitry Andric // - `NSString* const` from Foundation 190b57cec5SDimitry Andric // - `CFBooleanRef` from Foundation 200b57cec5SDimitry Andric // 210b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 220b57cec5SDimitry Andric 230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 240b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 250b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 260b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 270b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 280b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 290b57cec5SDimitry Andric 300b57cec5SDimitry Andric using namespace clang; 310b57cec5SDimitry Andric using namespace ento; 320b57cec5SDimitry Andric 330b57cec5SDimitry Andric namespace { 340b57cec5SDimitry Andric 350b57cec5SDimitry Andric class NonnullGlobalConstantsChecker : public Checker<check::Location> { 360b57cec5SDimitry Andric mutable IdentifierInfo *NSStringII = nullptr; 370b57cec5SDimitry Andric mutable IdentifierInfo *CFStringRefII = nullptr; 380b57cec5SDimitry Andric mutable IdentifierInfo *CFBooleanRefII = nullptr; 39480093f4SDimitry Andric mutable IdentifierInfo *CFNullRefII = nullptr; 400b57cec5SDimitry Andric 410b57cec5SDimitry Andric public: 420b57cec5SDimitry Andric NonnullGlobalConstantsChecker() {} 430b57cec5SDimitry Andric 440b57cec5SDimitry Andric void checkLocation(SVal l, bool isLoad, const Stmt *S, 450b57cec5SDimitry Andric CheckerContext &C) const; 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric private: 480b57cec5SDimitry Andric void initIdentifierInfo(ASTContext &Ctx) const; 490b57cec5SDimitry Andric 500b57cec5SDimitry Andric bool isGlobalConstString(SVal V) const; 510b57cec5SDimitry Andric 520b57cec5SDimitry Andric bool isNonnullType(QualType Ty) const; 530b57cec5SDimitry Andric }; 540b57cec5SDimitry Andric 550b57cec5SDimitry Andric } // namespace 560b57cec5SDimitry Andric 570b57cec5SDimitry Andric /// Lazily initialize cache for required identifier information. 580b57cec5SDimitry Andric void NonnullGlobalConstantsChecker::initIdentifierInfo(ASTContext &Ctx) const { 590b57cec5SDimitry Andric if (NSStringII) 600b57cec5SDimitry Andric return; 610b57cec5SDimitry Andric 620b57cec5SDimitry Andric NSStringII = &Ctx.Idents.get("NSString"); 630b57cec5SDimitry Andric CFStringRefII = &Ctx.Idents.get("CFStringRef"); 640b57cec5SDimitry Andric CFBooleanRefII = &Ctx.Idents.get("CFBooleanRef"); 65480093f4SDimitry Andric CFNullRefII = &Ctx.Idents.get("CFNullRef"); 660b57cec5SDimitry Andric } 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric /// Add an assumption that const string-like globals are non-null. 690b57cec5SDimitry Andric void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad, 700b57cec5SDimitry Andric const Stmt *S, 710b57cec5SDimitry Andric CheckerContext &C) const { 720b57cec5SDimitry Andric initIdentifierInfo(C.getASTContext()); 730b57cec5SDimitry Andric if (!isLoad || !location.isValid()) 740b57cec5SDimitry Andric return; 750b57cec5SDimitry Andric 760b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 770b57cec5SDimitry Andric 780b57cec5SDimitry Andric if (isGlobalConstString(location)) { 790b57cec5SDimitry Andric SVal V = State->getSVal(location.castAs<Loc>()); 800b57cec5SDimitry Andric Optional<DefinedOrUnknownSVal> Constr = V.getAs<DefinedOrUnknownSVal>(); 810b57cec5SDimitry Andric 820b57cec5SDimitry Andric if (Constr) { 830b57cec5SDimitry Andric 840b57cec5SDimitry Andric // Assume that the variable is non-null. 850b57cec5SDimitry Andric ProgramStateRef OutputState = State->assume(*Constr, true); 860b57cec5SDimitry Andric C.addTransition(OutputState); 870b57cec5SDimitry Andric } 880b57cec5SDimitry Andric } 890b57cec5SDimitry Andric } 900b57cec5SDimitry Andric 910b57cec5SDimitry Andric /// \param V loaded lvalue. 920b57cec5SDimitry Andric /// \return whether {@code val} is a string-like const global. 930b57cec5SDimitry Andric bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const { 940b57cec5SDimitry Andric Optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>(); 950b57cec5SDimitry Andric if (!RegionVal) 960b57cec5SDimitry Andric return false; 970b57cec5SDimitry Andric auto *Region = dyn_cast<VarRegion>(RegionVal->getAsRegion()); 980b57cec5SDimitry Andric if (!Region) 990b57cec5SDimitry Andric return false; 1000b57cec5SDimitry Andric const VarDecl *Decl = Region->getDecl(); 1010b57cec5SDimitry Andric 1020b57cec5SDimitry Andric if (!Decl->hasGlobalStorage()) 1030b57cec5SDimitry Andric return false; 1040b57cec5SDimitry Andric 1050b57cec5SDimitry Andric QualType Ty = Decl->getType(); 1060b57cec5SDimitry Andric bool HasConst = Ty.isConstQualified(); 1070b57cec5SDimitry Andric if (isNonnullType(Ty) && HasConst) 1080b57cec5SDimitry Andric return true; 1090b57cec5SDimitry Andric 1100b57cec5SDimitry Andric // Look through the typedefs. 1110b57cec5SDimitry Andric while (const Type *T = Ty.getTypePtr()) { 1120b57cec5SDimitry Andric if (const auto *TT = dyn_cast<TypedefType>(T)) { 1130b57cec5SDimitry Andric Ty = TT->getDecl()->getUnderlyingType(); 1140b57cec5SDimitry Andric // It is sufficient for any intermediate typedef 1150b57cec5SDimitry Andric // to be classified const. 1160b57cec5SDimitry Andric HasConst = HasConst || Ty.isConstQualified(); 1170b57cec5SDimitry Andric if (isNonnullType(Ty) && HasConst) 1180b57cec5SDimitry Andric return true; 1190b57cec5SDimitry Andric } else if (const auto *AT = dyn_cast<AttributedType>(T)) { 1200b57cec5SDimitry Andric if (AT->getAttrKind() == attr::TypeNonNull) 1210b57cec5SDimitry Andric return true; 1220b57cec5SDimitry Andric Ty = AT->getModifiedType(); 1230b57cec5SDimitry Andric } else { 1240b57cec5SDimitry Andric return false; 1250b57cec5SDimitry Andric } 1260b57cec5SDimitry Andric } 1270b57cec5SDimitry Andric return false; 1280b57cec5SDimitry Andric } 1290b57cec5SDimitry Andric 1300b57cec5SDimitry Andric /// \return whether {@code type} is extremely unlikely to be null 1310b57cec5SDimitry Andric bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const { 1320b57cec5SDimitry Andric 1330b57cec5SDimitry Andric if (Ty->isPointerType() && Ty->getPointeeType()->isCharType()) 1340b57cec5SDimitry Andric return true; 1350b57cec5SDimitry Andric 1360b57cec5SDimitry Andric if (auto *T = dyn_cast<ObjCObjectPointerType>(Ty)) { 1370b57cec5SDimitry Andric return T->getInterfaceDecl() && 1380b57cec5SDimitry Andric T->getInterfaceDecl()->getIdentifier() == NSStringII; 1390b57cec5SDimitry Andric } else if (auto *T = dyn_cast<TypedefType>(Ty)) { 1400b57cec5SDimitry Andric IdentifierInfo* II = T->getDecl()->getIdentifier(); 141480093f4SDimitry Andric return II == CFStringRefII || II == CFBooleanRefII || II == CFNullRefII; 1420b57cec5SDimitry Andric } 1430b57cec5SDimitry Andric return false; 1440b57cec5SDimitry Andric } 1450b57cec5SDimitry Andric 1460b57cec5SDimitry Andric void ento::registerNonnullGlobalConstantsChecker(CheckerManager &Mgr) { 1470b57cec5SDimitry Andric Mgr.registerChecker<NonnullGlobalConstantsChecker>(); 1480b57cec5SDimitry Andric } 1490b57cec5SDimitry Andric 150*5ffd83dbSDimitry Andric bool ento::shouldRegisterNonnullGlobalConstantsChecker(const CheckerManager &mgr) { 1510b57cec5SDimitry Andric return true; 1520b57cec5SDimitry Andric } 153