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