xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // An AST checker that looks for common pitfalls when using 'CFArray',
107330f729Sjoerg // 'CFDictionary', 'CFSet' APIs.
117330f729Sjoerg //
127330f729Sjoerg //===----------------------------------------------------------------------===//
137330f729Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
147330f729Sjoerg #include "clang/AST/StmtVisitor.h"
157330f729Sjoerg #include "clang/Analysis/AnalysisDeclContext.h"
167330f729Sjoerg #include "clang/Basic/TargetInfo.h"
177330f729Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
187330f729Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
197330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
207330f729Sjoerg #include "llvm/ADT/SmallString.h"
217330f729Sjoerg #include "llvm/Support/raw_ostream.h"
227330f729Sjoerg 
237330f729Sjoerg using namespace clang;
247330f729Sjoerg using namespace ento;
257330f729Sjoerg 
267330f729Sjoerg namespace {
277330f729Sjoerg class WalkAST : public StmtVisitor<WalkAST> {
287330f729Sjoerg   BugReporter &BR;
297330f729Sjoerg   const CheckerBase *Checker;
307330f729Sjoerg   AnalysisDeclContext* AC;
317330f729Sjoerg   ASTContext &ASTC;
327330f729Sjoerg   uint64_t PtrWidth;
337330f729Sjoerg 
347330f729Sjoerg   /// Check if the type has pointer size (very conservative).
isPointerSize(const Type * T)357330f729Sjoerg   inline bool isPointerSize(const Type *T) {
367330f729Sjoerg     if (!T)
377330f729Sjoerg       return true;
387330f729Sjoerg     if (T->isIncompleteType())
397330f729Sjoerg       return true;
407330f729Sjoerg     return (ASTC.getTypeSize(T) == PtrWidth);
417330f729Sjoerg   }
427330f729Sjoerg 
437330f729Sjoerg   /// Check if the type is a pointer/array to pointer sized values.
hasPointerToPointerSizedType(const Expr * E)447330f729Sjoerg   inline bool hasPointerToPointerSizedType(const Expr *E) {
457330f729Sjoerg     QualType T = E->getType();
467330f729Sjoerg 
477330f729Sjoerg     // The type could be either a pointer or array.
487330f729Sjoerg     const Type *TP = T.getTypePtr();
497330f729Sjoerg     QualType PointeeT = TP->getPointeeType();
507330f729Sjoerg     if (!PointeeT.isNull()) {
517330f729Sjoerg       // If the type is a pointer to an array, check the size of the array
527330f729Sjoerg       // elements. To avoid false positives coming from assumption that the
537330f729Sjoerg       // values x and &x are equal when x is an array.
547330f729Sjoerg       if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual())
557330f729Sjoerg         if (isPointerSize(TElem))
567330f729Sjoerg           return true;
577330f729Sjoerg 
587330f729Sjoerg       // Else, check the pointee size.
597330f729Sjoerg       return isPointerSize(PointeeT.getTypePtr());
607330f729Sjoerg     }
617330f729Sjoerg 
627330f729Sjoerg     if (const Type *TElem = TP->getArrayElementTypeNoTypeQual())
637330f729Sjoerg       return isPointerSize(TElem);
647330f729Sjoerg 
657330f729Sjoerg     // The type must be an array/pointer type.
667330f729Sjoerg 
677330f729Sjoerg     // This could be a null constant, which is allowed.
687330f729Sjoerg     return static_cast<bool>(
697330f729Sjoerg         E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull));
707330f729Sjoerg   }
717330f729Sjoerg 
727330f729Sjoerg public:
WalkAST(BugReporter & br,const CheckerBase * checker,AnalysisDeclContext * ac)737330f729Sjoerg   WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)
747330f729Sjoerg       : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()),
757330f729Sjoerg         PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {}
767330f729Sjoerg 
777330f729Sjoerg   // Statement visitor methods.
787330f729Sjoerg   void VisitChildren(Stmt *S);
VisitStmt(Stmt * S)797330f729Sjoerg   void VisitStmt(Stmt *S) { VisitChildren(S); }
807330f729Sjoerg   void VisitCallExpr(CallExpr *CE);
817330f729Sjoerg };
827330f729Sjoerg } // end anonymous namespace
837330f729Sjoerg 
getCalleeName(CallExpr * CE)847330f729Sjoerg static StringRef getCalleeName(CallExpr *CE) {
857330f729Sjoerg   const FunctionDecl *FD = CE->getDirectCallee();
867330f729Sjoerg   if (!FD)
877330f729Sjoerg     return StringRef();
887330f729Sjoerg 
897330f729Sjoerg   IdentifierInfo *II = FD->getIdentifier();
907330f729Sjoerg   if (!II)   // if no identifier, not a simple C function
917330f729Sjoerg     return StringRef();
927330f729Sjoerg 
937330f729Sjoerg   return II->getName();
947330f729Sjoerg }
957330f729Sjoerg 
VisitCallExpr(CallExpr * CE)967330f729Sjoerg void WalkAST::VisitCallExpr(CallExpr *CE) {
977330f729Sjoerg   StringRef Name = getCalleeName(CE);
987330f729Sjoerg   if (Name.empty())
997330f729Sjoerg     return;
1007330f729Sjoerg 
1017330f729Sjoerg   const Expr *Arg = nullptr;
1027330f729Sjoerg   unsigned ArgNum;
1037330f729Sjoerg 
1047330f729Sjoerg   if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) {
1057330f729Sjoerg     if (CE->getNumArgs() != 4)
1067330f729Sjoerg       return;
1077330f729Sjoerg     ArgNum = 1;
1087330f729Sjoerg     Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
1097330f729Sjoerg     if (hasPointerToPointerSizedType(Arg))
1107330f729Sjoerg         return;
1117330f729Sjoerg   } else if (Name.equals("CFDictionaryCreate")) {
1127330f729Sjoerg     if (CE->getNumArgs() != 6)
1137330f729Sjoerg       return;
1147330f729Sjoerg     // Check first argument.
1157330f729Sjoerg     ArgNum = 1;
1167330f729Sjoerg     Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
1177330f729Sjoerg     if (hasPointerToPointerSizedType(Arg)) {
1187330f729Sjoerg       // Check second argument.
1197330f729Sjoerg       ArgNum = 2;
1207330f729Sjoerg       Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
1217330f729Sjoerg       if (hasPointerToPointerSizedType(Arg))
1227330f729Sjoerg         // Both are good, return.
1237330f729Sjoerg         return;
1247330f729Sjoerg     }
1257330f729Sjoerg   }
1267330f729Sjoerg 
1277330f729Sjoerg   if (Arg) {
1287330f729Sjoerg     assert(ArgNum == 1 || ArgNum == 2);
1297330f729Sjoerg 
1307330f729Sjoerg     SmallString<64> BufName;
1317330f729Sjoerg     llvm::raw_svector_ostream OsName(BufName);
1327330f729Sjoerg     OsName << " Invalid use of '" << Name << "'" ;
1337330f729Sjoerg 
1347330f729Sjoerg     SmallString<256> Buf;
1357330f729Sjoerg     llvm::raw_svector_ostream Os(Buf);
1367330f729Sjoerg     // Use "second" and "third" since users will expect 1-based indexing
1377330f729Sjoerg     // for parameter names when mentioned in prose.
1387330f729Sjoerg     Os << " The "<< ((ArgNum == 1) ? "second" : "third") << " argument to '"
1397330f729Sjoerg         << Name << "' must be a C array of pointer-sized values, not '"
1407330f729Sjoerg         << Arg->getType().getAsString() << "'";
1417330f729Sjoerg 
1427330f729Sjoerg     PathDiagnosticLocation CELoc =
1437330f729Sjoerg         PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
1447330f729Sjoerg     BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(),
1457330f729Sjoerg                        categories::CoreFoundationObjectiveC, Os.str(), CELoc,
1467330f729Sjoerg                        Arg->getSourceRange());
1477330f729Sjoerg   }
1487330f729Sjoerg 
1497330f729Sjoerg   // Recurse and check children.
1507330f729Sjoerg   VisitChildren(CE);
1517330f729Sjoerg }
1527330f729Sjoerg 
VisitChildren(Stmt * S)1537330f729Sjoerg void WalkAST::VisitChildren(Stmt *S) {
1547330f729Sjoerg   for (Stmt *Child : S->children())
1557330f729Sjoerg     if (Child)
1567330f729Sjoerg       Visit(Child);
1577330f729Sjoerg }
1587330f729Sjoerg 
1597330f729Sjoerg namespace {
1607330f729Sjoerg class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> {
1617330f729Sjoerg public:
1627330f729Sjoerg 
checkASTCodeBody(const Decl * D,AnalysisManager & Mgr,BugReporter & BR) const1637330f729Sjoerg   void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
1647330f729Sjoerg                         BugReporter &BR) const {
1657330f729Sjoerg     WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D));
1667330f729Sjoerg     walker.Visit(D->getBody());
1677330f729Sjoerg   }
1687330f729Sjoerg };
1697330f729Sjoerg }
1707330f729Sjoerg 
registerObjCContainersASTChecker(CheckerManager & mgr)1717330f729Sjoerg void ento::registerObjCContainersASTChecker(CheckerManager &mgr) {
1727330f729Sjoerg   mgr.registerChecker<ObjCContainersASTChecker>();
1737330f729Sjoerg }
1747330f729Sjoerg 
shouldRegisterObjCContainersASTChecker(const CheckerManager & mgr)175*e038c9c4Sjoerg bool ento::shouldRegisterObjCContainersASTChecker(const CheckerManager &mgr) {
1767330f729Sjoerg   return true;
1777330f729Sjoerg }
178