10b57cec5SDimitry Andric //== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // An AST checker that looks for common pitfalls when using 'CFArray',
100b57cec5SDimitry Andric // 'CFDictionary', 'CFSet' APIs.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
140b57cec5SDimitry Andric #include "clang/AST/StmtVisitor.h"
150b57cec5SDimitry Andric #include "clang/Analysis/AnalysisDeclContext.h"
160b57cec5SDimitry Andric #include "clang/Basic/TargetInfo.h"
170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
200b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
210b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric using namespace clang;
240b57cec5SDimitry Andric using namespace ento;
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric namespace {
270b57cec5SDimitry Andric class WalkAST : public StmtVisitor<WalkAST> {
280b57cec5SDimitry Andric   BugReporter &BR;
290b57cec5SDimitry Andric   const CheckerBase *Checker;
300b57cec5SDimitry Andric   AnalysisDeclContext* AC;
310b57cec5SDimitry Andric   ASTContext &ASTC;
320b57cec5SDimitry Andric   uint64_t PtrWidth;
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric   /// Check if the type has pointer size (very conservative).
isPointerSize(const Type * T)350b57cec5SDimitry Andric   inline bool isPointerSize(const Type *T) {
360b57cec5SDimitry Andric     if (!T)
370b57cec5SDimitry Andric       return true;
380b57cec5SDimitry Andric     if (T->isIncompleteType())
390b57cec5SDimitry Andric       return true;
400b57cec5SDimitry Andric     return (ASTC.getTypeSize(T) == PtrWidth);
410b57cec5SDimitry Andric   }
420b57cec5SDimitry Andric 
430b57cec5SDimitry Andric   /// Check if the type is a pointer/array to pointer sized values.
hasPointerToPointerSizedType(const Expr * E)440b57cec5SDimitry Andric   inline bool hasPointerToPointerSizedType(const Expr *E) {
450b57cec5SDimitry Andric     QualType T = E->getType();
460b57cec5SDimitry Andric 
470b57cec5SDimitry Andric     // The type could be either a pointer or array.
480b57cec5SDimitry Andric     const Type *TP = T.getTypePtr();
490b57cec5SDimitry Andric     QualType PointeeT = TP->getPointeeType();
500b57cec5SDimitry Andric     if (!PointeeT.isNull()) {
510b57cec5SDimitry Andric       // If the type is a pointer to an array, check the size of the array
520b57cec5SDimitry Andric       // elements. To avoid false positives coming from assumption that the
530b57cec5SDimitry Andric       // values x and &x are equal when x is an array.
540b57cec5SDimitry Andric       if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual())
550b57cec5SDimitry Andric         if (isPointerSize(TElem))
560b57cec5SDimitry Andric           return true;
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric       // Else, check the pointee size.
590b57cec5SDimitry Andric       return isPointerSize(PointeeT.getTypePtr());
600b57cec5SDimitry Andric     }
610b57cec5SDimitry Andric 
620b57cec5SDimitry Andric     if (const Type *TElem = TP->getArrayElementTypeNoTypeQual())
630b57cec5SDimitry Andric       return isPointerSize(TElem);
640b57cec5SDimitry Andric 
650b57cec5SDimitry Andric     // The type must be an array/pointer type.
660b57cec5SDimitry Andric 
670b57cec5SDimitry Andric     // This could be a null constant, which is allowed.
680b57cec5SDimitry Andric     return static_cast<bool>(
690b57cec5SDimitry Andric         E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull));
700b57cec5SDimitry Andric   }
710b57cec5SDimitry Andric 
720b57cec5SDimitry Andric public:
WalkAST(BugReporter & br,const CheckerBase * checker,AnalysisDeclContext * ac)730b57cec5SDimitry Andric   WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)
740b57cec5SDimitry Andric       : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()),
75bdd1243dSDimitry Andric         PtrWidth(ASTC.getTargetInfo().getPointerWidth(LangAS::Default)) {}
760b57cec5SDimitry Andric 
770b57cec5SDimitry Andric   // Statement visitor methods.
780b57cec5SDimitry Andric   void VisitChildren(Stmt *S);
VisitStmt(Stmt * S)790b57cec5SDimitry Andric   void VisitStmt(Stmt *S) { VisitChildren(S); }
800b57cec5SDimitry Andric   void VisitCallExpr(CallExpr *CE);
810b57cec5SDimitry Andric };
820b57cec5SDimitry Andric } // end anonymous namespace
830b57cec5SDimitry Andric 
getCalleeName(CallExpr * CE)840b57cec5SDimitry Andric static StringRef getCalleeName(CallExpr *CE) {
850b57cec5SDimitry Andric   const FunctionDecl *FD = CE->getDirectCallee();
860b57cec5SDimitry Andric   if (!FD)
870b57cec5SDimitry Andric     return StringRef();
880b57cec5SDimitry Andric 
890b57cec5SDimitry Andric   IdentifierInfo *II = FD->getIdentifier();
900b57cec5SDimitry Andric   if (!II)   // if no identifier, not a simple C function
910b57cec5SDimitry Andric     return StringRef();
920b57cec5SDimitry Andric 
930b57cec5SDimitry Andric   return II->getName();
940b57cec5SDimitry Andric }
950b57cec5SDimitry Andric 
VisitCallExpr(CallExpr * CE)960b57cec5SDimitry Andric void WalkAST::VisitCallExpr(CallExpr *CE) {
970b57cec5SDimitry Andric   StringRef Name = getCalleeName(CE);
980b57cec5SDimitry Andric   if (Name.empty())
990b57cec5SDimitry Andric     return;
1000b57cec5SDimitry Andric 
1010b57cec5SDimitry Andric   const Expr *Arg = nullptr;
1020b57cec5SDimitry Andric   unsigned ArgNum;
1030b57cec5SDimitry Andric 
1040b57cec5SDimitry Andric   if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) {
1050b57cec5SDimitry Andric     if (CE->getNumArgs() != 4)
1060b57cec5SDimitry Andric       return;
1070b57cec5SDimitry Andric     ArgNum = 1;
1080b57cec5SDimitry Andric     Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
1090b57cec5SDimitry Andric     if (hasPointerToPointerSizedType(Arg))
1100b57cec5SDimitry Andric         return;
1110b57cec5SDimitry Andric   } else if (Name.equals("CFDictionaryCreate")) {
1120b57cec5SDimitry Andric     if (CE->getNumArgs() != 6)
1130b57cec5SDimitry Andric       return;
1140b57cec5SDimitry Andric     // Check first argument.
1150b57cec5SDimitry Andric     ArgNum = 1;
1160b57cec5SDimitry Andric     Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
1170b57cec5SDimitry Andric     if (hasPointerToPointerSizedType(Arg)) {
1180b57cec5SDimitry Andric       // Check second argument.
1190b57cec5SDimitry Andric       ArgNum = 2;
1200b57cec5SDimitry Andric       Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
1210b57cec5SDimitry Andric       if (hasPointerToPointerSizedType(Arg))
1220b57cec5SDimitry Andric         // Both are good, return.
1230b57cec5SDimitry Andric         return;
1240b57cec5SDimitry Andric     }
1250b57cec5SDimitry Andric   }
1260b57cec5SDimitry Andric 
1270b57cec5SDimitry Andric   if (Arg) {
1280b57cec5SDimitry Andric     assert(ArgNum == 1 || ArgNum == 2);
1290b57cec5SDimitry Andric 
1300b57cec5SDimitry Andric     SmallString<64> BufName;
1310b57cec5SDimitry Andric     llvm::raw_svector_ostream OsName(BufName);
1320b57cec5SDimitry Andric     OsName << " Invalid use of '" << Name << "'" ;
1330b57cec5SDimitry Andric 
1340b57cec5SDimitry Andric     SmallString<256> Buf;
1350b57cec5SDimitry Andric     llvm::raw_svector_ostream Os(Buf);
1360b57cec5SDimitry Andric     // Use "second" and "third" since users will expect 1-based indexing
1370b57cec5SDimitry Andric     // for parameter names when mentioned in prose.
1380b57cec5SDimitry Andric     Os << " The " << ((ArgNum == 1) ? "second" : "third") << " argument to '"
1390b57cec5SDimitry Andric        << Name << "' must be a C array of pointer-sized values, not '"
14081ad6265SDimitry Andric        << Arg->getType() << "'";
1410b57cec5SDimitry Andric 
1420b57cec5SDimitry Andric     PathDiagnosticLocation CELoc =
1430b57cec5SDimitry Andric         PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
1440b57cec5SDimitry Andric     BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(),
1450b57cec5SDimitry Andric                        categories::CoreFoundationObjectiveC, Os.str(), CELoc,
1460b57cec5SDimitry Andric                        Arg->getSourceRange());
1470b57cec5SDimitry Andric   }
1480b57cec5SDimitry Andric 
1490b57cec5SDimitry Andric   // Recurse and check children.
1500b57cec5SDimitry Andric   VisitChildren(CE);
1510b57cec5SDimitry Andric }
1520b57cec5SDimitry Andric 
VisitChildren(Stmt * S)1530b57cec5SDimitry Andric void WalkAST::VisitChildren(Stmt *S) {
1540b57cec5SDimitry Andric   for (Stmt *Child : S->children())
1550b57cec5SDimitry Andric     if (Child)
1560b57cec5SDimitry Andric       Visit(Child);
1570b57cec5SDimitry Andric }
1580b57cec5SDimitry Andric 
1590b57cec5SDimitry Andric namespace {
1600b57cec5SDimitry Andric class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> {
1610b57cec5SDimitry Andric public:
1620b57cec5SDimitry Andric 
checkASTCodeBody(const Decl * D,AnalysisManager & Mgr,BugReporter & BR) const1630b57cec5SDimitry Andric   void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
1640b57cec5SDimitry Andric                         BugReporter &BR) const {
1650b57cec5SDimitry Andric     WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D));
1660b57cec5SDimitry Andric     walker.Visit(D->getBody());
1670b57cec5SDimitry Andric   }
1680b57cec5SDimitry Andric };
1690b57cec5SDimitry Andric }
1700b57cec5SDimitry Andric 
registerObjCContainersASTChecker(CheckerManager & mgr)1710b57cec5SDimitry Andric void ento::registerObjCContainersASTChecker(CheckerManager &mgr) {
1720b57cec5SDimitry Andric   mgr.registerChecker<ObjCContainersASTChecker>();
1730b57cec5SDimitry Andric }
1740b57cec5SDimitry Andric 
shouldRegisterObjCContainersASTChecker(const CheckerManager & mgr)1755ffd83dbSDimitry Andric bool ento::shouldRegisterObjCContainersASTChecker(const CheckerManager &mgr) {
1760b57cec5SDimitry Andric   return true;
1770b57cec5SDimitry Andric }
178