ObjCContainersASTChecker.cpp revision 263508
11539Srgrimes//== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==// 21539Srgrimes// 31539Srgrimes// The LLVM Compiler Infrastructure 41539Srgrimes// 51539Srgrimes// This file is distributed under the University of Illinois Open Source 61539Srgrimes// License. See LICENSE.TXT for details. 71539Srgrimes// 81539Srgrimes//===----------------------------------------------------------------------===// 91539Srgrimes// 101539Srgrimes// An AST checker that looks for common pitfalls when using 'CFArray', 111539Srgrimes// 'CFDictionary', 'CFSet' APIs. 121539Srgrimes// 13203964Simp//===----------------------------------------------------------------------===// 141539Srgrimes#include "ClangSACheckers.h" 151539Srgrimes#include "clang/AST/StmtVisitor.h" 161539Srgrimes#include "clang/Analysis/AnalysisContext.h" 171539Srgrimes#include "clang/Basic/TargetInfo.h" 181539Srgrimes#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 191539Srgrimes#include "clang/StaticAnalyzer/Core/Checker.h" 201539Srgrimes#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 211539Srgrimes#include "llvm/ADT/SmallString.h" 221539Srgrimes#include "llvm/Support/raw_ostream.h" 231539Srgrimes 241539Srgrimesusing namespace clang; 251539Srgrimesusing namespace ento; 261539Srgrimes 271539Srgrimesnamespace { 281539Srgrimesclass WalkAST : public StmtVisitor<WalkAST> { 291539Srgrimes BugReporter &BR; 30203964Simp AnalysisDeclContext* AC; 31203964Simp ASTContext &ASTC; 321539Srgrimes uint64_t PtrWidth; 331539Srgrimes 341539Srgrimes /// Check if the type has pointer size (very conservative). 35 inline bool isPointerSize(const Type *T) { 36 if (!T) 37 return true; 38 if (T->isIncompleteType()) 39 return true; 40 return (ASTC.getTypeSize(T) == PtrWidth); 41 } 42 43 /// Check if the type is a pointer/array to pointer sized values. 44 inline bool hasPointerToPointerSizedType(const Expr *E) { 45 QualType T = E->getType(); 46 47 // The type could be either a pointer or array. 48 const Type *TP = T.getTypePtr(); 49 QualType PointeeT = TP->getPointeeType(); 50 if (!PointeeT.isNull()) { 51 // If the type is a pointer to an array, check the size of the array 52 // elements. To avoid false positives coming from assumption that the 53 // values x and &x are equal when x is an array. 54 if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual()) 55 if (isPointerSize(TElem)) 56 return true; 57 58 // Else, check the pointee size. 59 return isPointerSize(PointeeT.getTypePtr()); 60 } 61 62 if (const Type *TElem = TP->getArrayElementTypeNoTypeQual()) 63 return isPointerSize(TElem); 64 65 // The type must be an array/pointer type. 66 67 // This could be a null constant, which is allowed. 68 if (E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull)) 69 return true; 70 return false; 71 } 72 73public: 74 WalkAST(BugReporter &br, AnalysisDeclContext* ac) 75 : BR(br), AC(ac), ASTC(AC->getASTContext()), 76 PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {} 77 78 // Statement visitor methods. 79 void VisitChildren(Stmt *S); 80 void VisitStmt(Stmt *S) { VisitChildren(S); } 81 void VisitCallExpr(CallExpr *CE); 82}; 83} // end anonymous namespace 84 85static StringRef getCalleeName(CallExpr *CE) { 86 const FunctionDecl *FD = CE->getDirectCallee(); 87 if (!FD) 88 return StringRef(); 89 90 IdentifierInfo *II = FD->getIdentifier(); 91 if (!II) // if no identifier, not a simple C function 92 return StringRef(); 93 94 return II->getName(); 95} 96 97void WalkAST::VisitCallExpr(CallExpr *CE) { 98 StringRef Name = getCalleeName(CE); 99 if (Name.empty()) 100 return; 101 102 const Expr *Arg = 0; 103 unsigned ArgNum; 104 105 if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) { 106 if (CE->getNumArgs() != 4) 107 return; 108 ArgNum = 1; 109 Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 110 if (hasPointerToPointerSizedType(Arg)) 111 return; 112 } else if (Name.equals("CFDictionaryCreate")) { 113 if (CE->getNumArgs() != 6) 114 return; 115 // Check first argument. 116 ArgNum = 1; 117 Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 118 if (hasPointerToPointerSizedType(Arg)) { 119 // Check second argument. 120 ArgNum = 2; 121 Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 122 if (hasPointerToPointerSizedType(Arg)) 123 // Both are good, return. 124 return; 125 } 126 } 127 128 if (Arg) { 129 assert(ArgNum == 1 || ArgNum == 2); 130 131 SmallString<64> BufName; 132 llvm::raw_svector_ostream OsName(BufName); 133 OsName << " Invalid use of '" << Name << "'" ; 134 135 SmallString<256> Buf; 136 llvm::raw_svector_ostream Os(Buf); 137 // Use "second" and "third" since users will expect 1-based indexing 138 // for parameter names when mentioned in prose. 139 Os << " The "<< ((ArgNum == 1) ? "second" : "third") << " argument to '" 140 << Name << "' must be a C array of pointer-sized values, not '" 141 << Arg->getType().getAsString() << "'"; 142 143 PathDiagnosticLocation CELoc = 144 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 145 BR.EmitBasicReport(AC->getDecl(), 146 OsName.str(), categories::CoreFoundationObjectiveC, 147 Os.str(), CELoc, Arg->getSourceRange()); 148 } 149 150 // Recurse and check children. 151 VisitChildren(CE); 152} 153 154void WalkAST::VisitChildren(Stmt *S) { 155 for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) 156 if (Stmt *child = *I) 157 Visit(child); 158} 159 160namespace { 161class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> { 162public: 163 164 void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, 165 BugReporter &BR) const { 166 WalkAST walker(BR, Mgr.getAnalysisDeclContext(D)); 167 walker.Visit(D->getBody()); 168 } 169}; 170} 171 172void ento::registerObjCContainersASTChecker(CheckerManager &mgr) { 173 mgr.registerChecker<ObjCContainersASTChecker>(); 174} 175