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; 30234287Sdim AnalysisDeclContext* AC; 31234287Sdim ASTContext &ASTC; 32234287Sdim uint64_t PtrWidth; 33234287Sdim 34234287Sdim /// Check if the type has pointer size (very conservative). 35234287Sdim inline bool isPointerSize(const Type *T) { 36234287Sdim if (!T) 37234287Sdim return true; 38234287Sdim if (T->isIncompleteType()) 39234287Sdim return true; 40234287Sdim return (ASTC.getTypeSize(T) == PtrWidth); 41234287Sdim } 42234287Sdim 43234287Sdim /// Check if the type is a pointer/array to pointer sized values. 44234287Sdim inline bool hasPointerToPointerSizedType(const Expr *E) { 45234287Sdim QualType T = E->getType(); 46234287Sdim 47234287Sdim // The type could be either a pointer or array. 48234287Sdim const Type *TP = T.getTypePtr(); 49234287Sdim QualType PointeeT = TP->getPointeeType(); 50234287Sdim if (!PointeeT.isNull()) { 51234287Sdim // If the type is a pointer to an array, check the size of the array 52234287Sdim // elements. To avoid false positives coming from assumption that the 53234287Sdim // values x and &x are equal when x is an array. 54234287Sdim if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual()) 55234287Sdim if (isPointerSize(TElem)) 56234287Sdim return true; 57234287Sdim 58234287Sdim // Else, check the pointee size. 59234287Sdim return isPointerSize(PointeeT.getTypePtr()); 60234287Sdim } 61234287Sdim 62234287Sdim if (const Type *TElem = TP->getArrayElementTypeNoTypeQual()) 63234287Sdim return isPointerSize(TElem); 64234287Sdim 65234287Sdim // The type must be an array/pointer type. 66234287Sdim 67234287Sdim // This could be a null constant, which is allowed. 68234287Sdim if (E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull)) 69234287Sdim return true; 70234287Sdim return false; 71234287Sdim } 72234287Sdim 73234287Sdimpublic: 74234287Sdim WalkAST(BugReporter &br, AnalysisDeclContext* ac) 75234287Sdim : BR(br), AC(ac), ASTC(AC->getASTContext()), 76234287Sdim 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 102234287Sdim const Expr *Arg = 0; 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); 145234287Sdim BR.EmitBasicReport(AC->getDecl(), 146234287Sdim OsName.str(), categories::CoreFoundationObjectiveC, 147263508Sdim Os.str(), CELoc, Arg->getSourceRange()); 148234287Sdim } 149234287Sdim 150234287Sdim // Recurse and check children. 151234287Sdim VisitChildren(CE); 152234287Sdim} 153234287Sdim 154234287Sdimvoid WalkAST::VisitChildren(Stmt *S) { 155234287Sdim for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) 156234287Sdim if (Stmt *child = *I) 157234287Sdim 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 { 166234287Sdim WalkAST walker(BR, Mgr.getAnalysisDeclContext(D)); 167234287Sdim walker.Visit(D->getBody()); 168234287Sdim } 169234287Sdim}; 170234287Sdim} 171234287Sdim 172234287Sdimvoid ento::registerObjCContainersASTChecker(CheckerManager &mgr) { 173234287Sdim mgr.registerChecker<ObjCContainersASTChecker>(); 174234287Sdim} 175