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