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). 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. 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: 730b57cec5SDimitry Andric WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac) 740b57cec5SDimitry Andric : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()), 750b57cec5SDimitry Andric PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {} 760b57cec5SDimitry Andric 770b57cec5SDimitry Andric // Statement visitor methods. 780b57cec5SDimitry Andric void VisitChildren(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 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 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 '" 140*81ad6265SDimitry 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 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 1630b57cec5SDimitry 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 1710b57cec5SDimitry Andric void ento::registerObjCContainersASTChecker(CheckerManager &mgr) { 1720b57cec5SDimitry Andric mgr.registerChecker<ObjCContainersASTChecker>(); 1730b57cec5SDimitry Andric } 1740b57cec5SDimitry Andric 1755ffd83dbSDimitry Andric bool ento::shouldRegisterObjCContainersASTChecker(const CheckerManager &mgr) { 1760b57cec5SDimitry Andric return true; 1770b57cec5SDimitry Andric } 178