1 //== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==//
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
7 //===----------------------------------------------------------------------===//
9 // An AST checker that looks for common pitfalls when using 'CFArray',
10 // 'CFDictionary', 'CFSet' APIs.
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"
23 using namespace clang
;
27 class WalkAST
: public StmtVisitor
<WalkAST
> {
29 const CheckerBase
*Checker
;
30 AnalysisDeclContext
* AC
;
34 /// Check if the type has pointer size (very conservative).
35 inline bool isPointerSize(const Type
*T
) {
38 if (T
->isIncompleteType())
40 return (ASTC
.getTypeSize(T
) == PtrWidth
);
43 /// Check if the type is a pointer/array to pointer sized values.
44 inline bool hasPointerToPointerSizedType(const Expr
*E
) {
45 QualType T
= E
->getType();
47 // The type could be either a pointer or array.
48 const Type
*TP
= T
.getTypePtr();
49 QualType PointeeT
= TP
->getPointeeType();
50 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 if (const Type
*TElem
= PointeeT
->getArrayElementTypeNoTypeQual())
55 if (isPointerSize(TElem
))
58 // Else, check the pointee size.
59 return isPointerSize(PointeeT
.getTypePtr());
62 if (const Type
*TElem
= TP
->getArrayElementTypeNoTypeQual())
63 return isPointerSize(TElem
);
65 // The type must be an array/pointer type.
67 // This could be a null constant, which is allowed.
68 return static_cast<bool>(
69 E
->isNullPointerConstant(ASTC
, Expr::NPC_ValueDependentIsNull
));
73 WalkAST(BugReporter
&br
, const CheckerBase
*checker
, AnalysisDeclContext
*ac
)
74 : BR(br
), Checker(checker
), AC(ac
), ASTC(AC
->getASTContext()),
75 PtrWidth(ASTC
.getTargetInfo().getPointerWidth(LangAS::Default
)) {}
77 // Statement visitor methods.
78 void VisitChildren(Stmt
*S
);
79 void VisitStmt(Stmt
*S
) { VisitChildren(S
); }
80 void VisitCallExpr(CallExpr
*CE
);
82 } // end anonymous namespace
84 static StringRef
getCalleeName(CallExpr
*CE
) {
85 const FunctionDecl
*FD
= CE
->getDirectCallee();
89 IdentifierInfo
*II
= FD
->getIdentifier();
90 if (!II
) // if no identifier, not a simple C function
96 void WalkAST::VisitCallExpr(CallExpr
*CE
) {
97 StringRef Name
= getCalleeName(CE
);
101 const Expr
*Arg
= nullptr;
104 if (Name
.equals("CFArrayCreate") || Name
.equals("CFSetCreate")) {
105 if (CE
->getNumArgs() != 4)
108 Arg
= CE
->getArg(ArgNum
)->IgnoreParenCasts();
109 if (hasPointerToPointerSizedType(Arg
))
111 } else if (Name
.equals("CFDictionaryCreate")) {
112 if (CE
->getNumArgs() != 6)
114 // Check first argument.
116 Arg
= CE
->getArg(ArgNum
)->IgnoreParenCasts();
117 if (hasPointerToPointerSizedType(Arg
)) {
118 // Check second argument.
120 Arg
= CE
->getArg(ArgNum
)->IgnoreParenCasts();
121 if (hasPointerToPointerSizedType(Arg
))
122 // Both are good, return.
128 assert(ArgNum
== 1 || ArgNum
== 2);
130 SmallString
<64> BufName
;
131 llvm::raw_svector_ostream
OsName(BufName
);
132 OsName
<< " Invalid use of '" << Name
<< "'" ;
134 SmallString
<256> Buf
;
135 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 Os
<< " The " << ((ArgNum
== 1) ? "second" : "third") << " argument to '"
139 << Name
<< "' must be a C array of pointer-sized values, not '"
140 << Arg
->getType() << "'";
142 PathDiagnosticLocation CELoc
=
143 PathDiagnosticLocation::createBegin(CE
, BR
.getSourceManager(), AC
);
144 BR
.EmitBasicReport(AC
->getDecl(), Checker
, OsName
.str(),
145 categories::CoreFoundationObjectiveC
, Os
.str(), CELoc
,
146 Arg
->getSourceRange());
149 // Recurse and check children.
153 void WalkAST::VisitChildren(Stmt
*S
) {
154 for (Stmt
*Child
: S
->children())
160 class ObjCContainersASTChecker
: public Checker
<check::ASTCodeBody
> {
163 void checkASTCodeBody(const Decl
*D
, AnalysisManager
& Mgr
,
164 BugReporter
&BR
) const {
165 WalkAST
walker(BR
, this, Mgr
.getAnalysisDeclContext(D
));
166 walker
.Visit(D
->getBody());
171 void ento::registerObjCContainersASTChecker(CheckerManager
&mgr
) {
172 mgr
.registerChecker
<ObjCContainersASTChecker
>();
175 bool ento::shouldRegisterObjCContainersASTChecker(const CheckerManager
&mgr
) {