154 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This checker adds an assumption that constant globals of certain types* are10// 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 the13// definition is in a separate translation unit.14// The following types (and their typedef aliases) are considered to be15// non-null:16// - `char* const`17// - `const CFStringRef` from CoreFoundation18// - `NSString* const` from Foundation19// - `CFBooleanRef` from Foundation20//21//===----------------------------------------------------------------------===//22 23#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"24#include "clang/StaticAnalyzer/Core/Checker.h"25#include "clang/StaticAnalyzer/Core/CheckerManager.h"26#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"27#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"28#include <optional>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} // namespace56 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 std::optional<DefinedOrUnknownSVal> Constr =81 V.getAs<DefinedOrUnknownSVal>();82 83 if (Constr) {84 85 // Assume that the variable is non-null.86 ProgramStateRef OutputState = State->assume(*Constr, true);87 C.addTransition(OutputState);88 }89 }90}91 92/// \param V loaded lvalue.93/// \return whether @c val is a string-like const global.94bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const {95 std::optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>();96 if (!RegionVal)97 return false;98 auto *Region = dyn_cast<VarRegion>(RegionVal->getAsRegion());99 if (!Region)100 return false;101 const VarDecl *Decl = Region->getDecl();102 103 if (!Decl->hasGlobalStorage())104 return false;105 106 QualType Ty = Decl->getType();107 bool HasConst = Ty.isConstQualified();108 if (isNonnullType(Ty) && HasConst)109 return true;110 111 // Look through the typedefs.112 while (const Type *T = Ty.getTypePtr()) {113 if (const auto *AT = dyn_cast<AttributedType>(T)) {114 if (AT->getAttrKind() == attr::TypeNonNull)115 return true;116 Ty = AT->getModifiedType();117 } else if (const auto *TT = dyn_cast<TypedefType>(T)) {118 Ty = TT->getDecl()->getUnderlyingType();119 // It is sufficient for any intermediate typedef120 // to be classified const.121 HasConst = HasConst || Ty.isConstQualified();122 if (isNonnullType(Ty) && HasConst)123 return true;124 } else {125 return false;126 }127 }128 return false;129}130 131/// \return whether @c type is extremely unlikely to be null132bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const {133 134 if (Ty->isPointerType() && Ty->getPointeeType()->isCharType())135 return true;136 137 if (auto *T = dyn_cast<ObjCObjectPointerType>(Ty)) {138 return T->getInterfaceDecl() &&139 T->getInterfaceDecl()->getIdentifier() == NSStringII;140 } else if (auto *T = Ty->getAs<TypedefType>()) {141 IdentifierInfo* II = T->getDecl()->getIdentifier();142 return II == CFStringRefII || II == CFBooleanRefII || II == CFNullRefII;143 }144 return false;145}146 147void ento::registerNonnullGlobalConstantsChecker(CheckerManager &Mgr) {148 Mgr.registerChecker<NonnullGlobalConstantsChecker>();149}150 151bool ento::shouldRegisterNonnullGlobalConstantsChecker(const CheckerManager &mgr) {152 return true;153}154