blob: 516544b0f5ce9b46806d6ce8495129469ec6391e [file] [log] [blame]
George Karpenkovead01622017-10-11 18:39:40 +00001//==- NonnullStringConstantsChecker.cpp ---------------------------*- C++ -*--//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This checker adds an assumption that constant string-like globals are
11// non-null, as otherwise they generally do not convey any useful information.
12// The assumption is useful, as many framework use such global const strings,
13// and the analyzer might not be able to infer the global value if the
14// definition is in a separate translation unit.
15// The following types (and their typedef aliases) are considered string-like:
16// - `char* const`
17// - `const CFStringRef` from CoreFoundation
18// - `NSString* const` from Foundation
19//
20//===----------------------------------------------------------------------===//
21
22#include "ClangSACheckers.h"
23#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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
29using namespace clang;
30using namespace ento;
31
32namespace {
33
34class NonnullStringConstantsChecker : public Checker<check::Location> {
35 mutable IdentifierInfo *NSStringII = nullptr;
36 mutable IdentifierInfo *CFStringRefII = nullptr;
37
38public:
39 NonnullStringConstantsChecker() {}
40
41 void checkLocation(SVal l, bool isLoad, const Stmt *S,
42 CheckerContext &C) const;
43
44private:
45 void initIdentifierInfo(ASTContext &Ctx) const;
46
47 bool isGlobalConstString(SVal V) const;
48
49 bool isStringlike(QualType Ty) const;
50};
51
52} // namespace
53
54/// Lazily initialize cache for required identifier informations.
55void NonnullStringConstantsChecker::initIdentifierInfo(ASTContext &Ctx) const {
56 if (NSStringII)
57 return;
58
59 NSStringII = &Ctx.Idents.get("NSString");
60 CFStringRefII = &Ctx.Idents.get("CFStringRef");
61}
62
63/// Add an assumption that const string-like globals are non-null.
64void NonnullStringConstantsChecker::checkLocation(SVal location, bool isLoad,
65 const Stmt *S,
66 CheckerContext &C) const {
67 initIdentifierInfo(C.getASTContext());
68 if (!isLoad || !location.isValid())
69 return;
70
71 ProgramStateRef State = C.getState();
72 SVal V = State->getSVal(location.castAs<Loc>());
73
74 if (isGlobalConstString(location)) {
75 Optional<DefinedOrUnknownSVal> Constr = V.getAs<DefinedOrUnknownSVal>();
76
77 if (Constr) {
78
79 // Assume that the variable is non-null.
80 ProgramStateRef OutputState = State->assume(*Constr, true);
81 C.addTransition(OutputState);
82 }
83 }
84}
85
86/// \param V loaded lvalue.
87/// \return whether {@code val} is a string-like const global.
88bool NonnullStringConstantsChecker::isGlobalConstString(SVal V) const {
89 Optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>();
90 if (!RegionVal)
91 return false;
92 auto *Region = dyn_cast<VarRegion>(RegionVal->getAsRegion());
93 if (!Region)
94 return false;
95 const VarDecl *Decl = Region->getDecl();
96
97 if (!Decl->hasGlobalStorage())
98 return false;
99
100 QualType Ty = Decl->getType();
101 bool HasConst = Ty.isConstQualified();
102 if (isStringlike(Ty) && HasConst)
103 return true;
104
105 // Look through the typedefs.
106 while (auto *T = dyn_cast<TypedefType>(Ty)) {
107 Ty = T->getDecl()->getUnderlyingType();
108
109 // It is sufficient for any intermediate typedef
110 // to be classified const.
111 HasConst = HasConst || Ty.isConstQualified();
112 if (isStringlike(Ty) && HasConst)
113 return true;
114 }
115 return false;
116}
117
118/// \return whether {@code type} is a string-like type.
119bool NonnullStringConstantsChecker::isStringlike(QualType Ty) const {
120
121 if (Ty->isPointerType() && Ty->getPointeeType()->isCharType())
122 return true;
123
124 if (auto *T = dyn_cast<ObjCObjectPointerType>(Ty)) {
George Karpenkov734cad82017-10-11 19:13:15 +0000125 return T->getInterfaceDecl() &&
126 T->getInterfaceDecl()->getIdentifier() == NSStringII;
George Karpenkovead01622017-10-11 18:39:40 +0000127 } else if (auto *T = dyn_cast<TypedefType>(Ty)) {
128 return T->getDecl()->getIdentifier() == CFStringRefII;
129 }
130 return false;
131}
132
133void ento::registerNonnullStringConstantsChecker(CheckerManager &Mgr) {
134 Mgr.registerChecker<NonnullStringConstantsChecker>();
135}