/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- 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-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // An AST checker that looks for common pitfalls when using 'CFArray', |
10 | | // 'CFDictionary', 'CFSet' APIs. |
11 | | // |
12 | | //===----------------------------------------------------------------------===// |
13 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
14 | | #include "clang/AST/StmtVisitor.h" |
15 | | #include "clang/Analysis/AnalysisDeclContext.h" |
16 | | #include "clang/Basic/TargetInfo.h" |
17 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
18 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
19 | | #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" |
20 | | #include "llvm/ADT/SmallString.h" |
21 | | #include "llvm/Support/raw_ostream.h" |
22 | | |
23 | | using namespace clang; |
24 | | using namespace ento; |
25 | | |
26 | | namespace { |
27 | | class WalkAST : public StmtVisitor<WalkAST> { |
28 | | BugReporter &BR; |
29 | | const CheckerBase *Checker; |
30 | | AnalysisDeclContext* AC; |
31 | | ASTContext &ASTC; |
32 | | uint64_t PtrWidth; |
33 | | |
34 | | /// Check if the type has pointer size (very conservative). |
35 | 21 | inline bool isPointerSize(const Type *T) { |
36 | 21 | if (!T) |
37 | 0 | return true; |
38 | 21 | if (T->isIncompleteType()) |
39 | 0 | return true; |
40 | 21 | return (ASTC.getTypeSize(T) == PtrWidth); |
41 | 21 | } |
42 | | |
43 | | /// Check if the type is a pointer/array to pointer sized values. |
44 | 24 | inline bool hasPointerToPointerSizedType(const Expr *E) { |
45 | 24 | QualType T = E->getType(); |
46 | | |
47 | | // The type could be either a pointer or array. |
48 | 24 | const Type *TP = T.getTypePtr(); |
49 | 24 | QualType PointeeT = TP->getPointeeType(); |
50 | 24 | if (!PointeeT.isNull()) { |
51 | | // If the type is a pointer to an array, check the size of the array |
52 | | // elements. To avoid false positives coming from assumption that the |
53 | | // values x and &x are equal when x is an array. |
54 | 13 | if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual()) |
55 | 3 | if (isPointerSize(TElem)) |
56 | 2 | return true; |
57 | | |
58 | | // Else, check the pointee size. |
59 | 11 | return isPointerSize(PointeeT.getTypePtr()); |
60 | 13 | } |
61 | | |
62 | 11 | if (const Type *TElem = TP->getArrayElementTypeNoTypeQual()) |
63 | 7 | return isPointerSize(TElem); |
64 | | |
65 | | // The type must be an array/pointer type. |
66 | | |
67 | | // This could be a null constant, which is allowed. |
68 | 4 | return static_cast<bool>( |
69 | 4 | E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull)); |
70 | 11 | } |
71 | | |
72 | | public: |
73 | | WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac) |
74 | 234 | : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()), |
75 | 234 | PtrWidth(ASTC.getTargetInfo().getPointerWidth(LangAS::Default)) {} |
76 | | |
77 | | // Statement visitor methods. |
78 | | void VisitChildren(Stmt *S); |
79 | 3.18k | void VisitStmt(Stmt *S) { VisitChildren(S); } |
80 | | void VisitCallExpr(CallExpr *CE); |
81 | | }; |
82 | | } // end anonymous namespace |
83 | | |
84 | 319 | static StringRef getCalleeName(CallExpr *CE) { |
85 | 319 | const FunctionDecl *FD = CE->getDirectCallee(); |
86 | 319 | if (!FD) |
87 | 2 | return StringRef(); |
88 | | |
89 | 317 | IdentifierInfo *II = FD->getIdentifier(); |
90 | 317 | if (!II) // if no identifier, not a simple C function |
91 | 0 | return StringRef(); |
92 | | |
93 | 317 | return II->getName(); |
94 | 317 | } |
95 | | |
96 | 319 | void WalkAST::VisitCallExpr(CallExpr *CE) { |
97 | 319 | StringRef Name = getCalleeName(CE); |
98 | 319 | if (Name.empty()) |
99 | 2 | return; |
100 | | |
101 | 317 | const Expr *Arg = nullptr; |
102 | 317 | unsigned ArgNum; |
103 | | |
104 | 317 | if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")299 ) { |
105 | 21 | if (CE->getNumArgs() != 4) |
106 | 2 | return; |
107 | 19 | ArgNum = 1; |
108 | 19 | Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); |
109 | 19 | if (hasPointerToPointerSizedType(Arg)) |
110 | 14 | return; |
111 | 296 | } else if (Name.equals("CFDictionaryCreate")) { |
112 | 4 | if (CE->getNumArgs() != 6) |
113 | 1 | return; |
114 | | // Check first argument. |
115 | 3 | ArgNum = 1; |
116 | 3 | Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); |
117 | 3 | if (hasPointerToPointerSizedType(Arg)) { |
118 | | // Check second argument. |
119 | 2 | ArgNum = 2; |
120 | 2 | Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); |
121 | 2 | if (hasPointerToPointerSizedType(Arg)) |
122 | | // Both are good, return. |
123 | 1 | return; |
124 | 2 | } |
125 | 3 | } |
126 | | |
127 | 299 | if (Arg) { |
128 | 7 | assert(ArgNum == 1 || ArgNum == 2); |
129 | | |
130 | 7 | SmallString<64> BufName; |
131 | 7 | llvm::raw_svector_ostream OsName(BufName); |
132 | 7 | OsName << " Invalid use of '" << Name << "'" ; |
133 | | |
134 | 7 | SmallString<256> Buf; |
135 | 7 | llvm::raw_svector_ostream Os(Buf); |
136 | | // Use "second" and "third" since users will expect 1-based indexing |
137 | | // for parameter names when mentioned in prose. |
138 | 7 | Os << " The " << ((ArgNum == 1) ? "second"6 : "third"1 ) << " argument to '" |
139 | 7 | << Name << "' must be a C array of pointer-sized values, not '" |
140 | 7 | << Arg->getType() << "'"; |
141 | | |
142 | 7 | PathDiagnosticLocation CELoc = |
143 | 7 | PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); |
144 | 7 | BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(), |
145 | 7 | categories::CoreFoundationObjectiveC, Os.str(), CELoc, |
146 | 7 | Arg->getSourceRange()); |
147 | 7 | } |
148 | | |
149 | | // Recurse and check children. |
150 | 299 | VisitChildren(CE); |
151 | 299 | } |
152 | | |
153 | 3.48k | void WalkAST::VisitChildren(Stmt *S) { |
154 | 3.48k | for (Stmt *Child : S->children()) |
155 | 3.26k | if (Child) |
156 | 3.26k | Visit(Child); |
157 | 3.48k | } |
158 | | |
159 | | namespace { |
160 | | class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> { |
161 | | public: |
162 | | |
163 | | void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, |
164 | 234 | BugReporter &BR) const { |
165 | 234 | WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D)); |
166 | 234 | walker.Visit(D->getBody()); |
167 | 234 | } |
168 | | }; |
169 | | } |
170 | | |
171 | 48 | void ento::registerObjCContainersASTChecker(CheckerManager &mgr) { |
172 | 48 | mgr.registerChecker<ObjCContainersASTChecker>(); |
173 | 48 | } |
174 | | |
175 | 96 | bool ento::shouldRegisterObjCContainersASTChecker(const CheckerManager &mgr) { |
176 | 96 | return true; |
177 | 96 | } |