1234287Sdim//== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==// 2234287Sdim// 3234287Sdim// The LLVM Compiler Infrastructure 4234287Sdim// 5234287Sdim// This file is distributed under the University of Illinois Open Source 6234287Sdim// License. See LICENSE.TXT for details. 7234287Sdim// 8234287Sdim//===----------------------------------------------------------------------===// 9234287Sdim// 10234287Sdim// An AST checker that looks for common pitfalls when using 'CFArray', 11234287Sdim// 'CFDictionary', 'CFSet' APIs. 12234287Sdim// 13234287Sdim//===----------------------------------------------------------------------===// 14234287Sdim#include "ClangSACheckers.h" 15249423Sdim#include "clang/AST/StmtVisitor.h" 16234287Sdim#include "clang/Analysis/AnalysisContext.h" 17234287Sdim#include "clang/Basic/TargetInfo.h" 18249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 19234287Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 20234287Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 21234287Sdim#include "llvm/ADT/SmallString.h" 22234287Sdim#include "llvm/Support/raw_ostream.h" 23234287Sdim 24234287Sdimusing namespace clang; 25234287Sdimusing namespace ento; 26234287Sdim 27234287Sdimnamespace { 28234287Sdimclass WalkAST : public StmtVisitor<WalkAST> { 29234287Sdim BugReporter &BR; 30276479Sdim const CheckerBase *Checker; 31234287Sdim AnalysisDeclContext* AC; 32234287Sdim ASTContext &ASTC; 33234287Sdim uint64_t PtrWidth; 34234287Sdim 35234287Sdim /// Check if the type has pointer size (very conservative). 36234287Sdim inline bool isPointerSize(const Type *T) { 37234287Sdim if (!T) 38234287Sdim return true; 39234287Sdim if (T->isIncompleteType()) 40234287Sdim return true; 41234287Sdim return (ASTC.getTypeSize(T) == PtrWidth); 42234287Sdim } 43234287Sdim 44234287Sdim /// Check if the type is a pointer/array to pointer sized values. 45234287Sdim inline bool hasPointerToPointerSizedType(const Expr *E) { 46234287Sdim QualType T = E->getType(); 47234287Sdim 48234287Sdim // The type could be either a pointer or array. 49234287Sdim const Type *TP = T.getTypePtr(); 50234287Sdim QualType PointeeT = TP->getPointeeType(); 51234287Sdim if (!PointeeT.isNull()) { 52234287Sdim // If the type is a pointer to an array, check the size of the array 53234287Sdim // elements. To avoid false positives coming from assumption that the 54234287Sdim // values x and &x are equal when x is an array. 55234287Sdim if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual()) 56234287Sdim if (isPointerSize(TElem)) 57234287Sdim return true; 58234287Sdim 59234287Sdim // Else, check the pointee size. 60234287Sdim return isPointerSize(PointeeT.getTypePtr()); 61234287Sdim } 62234287Sdim 63234287Sdim if (const Type *TElem = TP->getArrayElementTypeNoTypeQual()) 64234287Sdim return isPointerSize(TElem); 65234287Sdim 66234287Sdim // The type must be an array/pointer type. 67234287Sdim 68234287Sdim // This could be a null constant, which is allowed. 69296417Sdim return static_cast<bool>( 70296417Sdim E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull)); 71234287Sdim } 72234287Sdim 73234287Sdimpublic: 74276479Sdim WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac) 75276479Sdim : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()), 76276479Sdim PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {} 77234287Sdim 78234287Sdim // Statement visitor methods. 79234287Sdim void VisitChildren(Stmt *S); 80234287Sdim void VisitStmt(Stmt *S) { VisitChildren(S); } 81234287Sdim void VisitCallExpr(CallExpr *CE); 82234287Sdim}; 83234287Sdim} // end anonymous namespace 84234287Sdim 85234287Sdimstatic StringRef getCalleeName(CallExpr *CE) { 86234287Sdim const FunctionDecl *FD = CE->getDirectCallee(); 87234287Sdim if (!FD) 88234287Sdim return StringRef(); 89234287Sdim 90234287Sdim IdentifierInfo *II = FD->getIdentifier(); 91234287Sdim if (!II) // if no identifier, not a simple C function 92234287Sdim return StringRef(); 93234287Sdim 94234287Sdim return II->getName(); 95234287Sdim} 96234287Sdim 97234287Sdimvoid WalkAST::VisitCallExpr(CallExpr *CE) { 98234287Sdim StringRef Name = getCalleeName(CE); 99234287Sdim if (Name.empty()) 100234287Sdim return; 101234287Sdim 102276479Sdim const Expr *Arg = nullptr; 103243830Sdim unsigned ArgNum; 104234287Sdim 105234287Sdim if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) { 106243830Sdim if (CE->getNumArgs() != 4) 107243830Sdim return; 108234287Sdim ArgNum = 1; 109234287Sdim Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 110234287Sdim if (hasPointerToPointerSizedType(Arg)) 111234287Sdim return; 112243830Sdim } else if (Name.equals("CFDictionaryCreate")) { 113243830Sdim if (CE->getNumArgs() != 6) 114243830Sdim return; 115234287Sdim // Check first argument. 116234287Sdim ArgNum = 1; 117234287Sdim Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 118234287Sdim if (hasPointerToPointerSizedType(Arg)) { 119234287Sdim // Check second argument. 120234287Sdim ArgNum = 2; 121234287Sdim Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 122234287Sdim if (hasPointerToPointerSizedType(Arg)) 123234287Sdim // Both are good, return. 124234287Sdim return; 125234287Sdim } 126234287Sdim } 127234287Sdim 128243830Sdim if (Arg) { 129234287Sdim assert(ArgNum == 1 || ArgNum == 2); 130234287Sdim 131243830Sdim SmallString<64> BufName; 132234287Sdim llvm::raw_svector_ostream OsName(BufName); 133234287Sdim OsName << " Invalid use of '" << Name << "'" ; 134234287Sdim 135234287Sdim SmallString<256> Buf; 136234287Sdim llvm::raw_svector_ostream Os(Buf); 137243830Sdim // Use "second" and "third" since users will expect 1-based indexing 138243830Sdim // for parameter names when mentioned in prose. 139243830Sdim Os << " The "<< ((ArgNum == 1) ? "second" : "third") << " argument to '" 140234287Sdim << Name << "' must be a C array of pointer-sized values, not '" 141234287Sdim << Arg->getType().getAsString() << "'"; 142234287Sdim 143234287Sdim PathDiagnosticLocation CELoc = 144234287Sdim PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 145276479Sdim BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(), 146276479Sdim categories::CoreFoundationObjectiveC, Os.str(), CELoc, 147276479Sdim Arg->getSourceRange()); 148234287Sdim } 149234287Sdim 150234287Sdim // Recurse and check children. 151234287Sdim VisitChildren(CE); 152234287Sdim} 153234287Sdim 154234287Sdimvoid WalkAST::VisitChildren(Stmt *S) { 155288943Sdim for (Stmt *Child : S->children()) 156288943Sdim if (Child) 157288943Sdim Visit(Child); 158234287Sdim} 159234287Sdim 160234287Sdimnamespace { 161234287Sdimclass ObjCContainersASTChecker : public Checker<check::ASTCodeBody> { 162234287Sdimpublic: 163234287Sdim 164234287Sdim void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, 165234287Sdim BugReporter &BR) const { 166276479Sdim WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D)); 167234287Sdim walker.Visit(D->getBody()); 168234287Sdim } 169234287Sdim}; 170234287Sdim} 171234287Sdim 172234287Sdimvoid ento::registerObjCContainersASTChecker(CheckerManager &mgr) { 173234287Sdim mgr.registerChecker<ObjCContainersASTChecker>(); 174234287Sdim} 175