xref: /aosp_15_r20/external/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp (revision 67e74705e28f6214e480b399dd47ea732279e315)
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