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