xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
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