ObjCContainersASTChecker.cpp revision 1efcc42c922204d6797a70d90d3c350882f3c098
1//== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// An AST checker that looks for common pitfalls when using 'CFArray', 11// 'CFDictionary', 'CFSet' APIs. 12// 13//===----------------------------------------------------------------------===// 14#include "ClangSACheckers.h" 15#include "clang/Analysis/AnalysisContext.h" 16#include "clang/AST/StmtVisitor.h" 17#include "clang/Basic/TargetInfo.h" 18#include "clang/StaticAnalyzer/Core/Checker.h" 19#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 20#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 21#include "llvm/Support/raw_ostream.h" 22 23using namespace clang; 24using namespace ento; 25 26namespace { 27class WalkAST : public StmtVisitor<WalkAST> { 28 BugReporter &BR; 29 AnalysisDeclContext* AC; 30 ASTContext &ASTC; 31 uint64_t PtrWidth; 32 33 static const unsigned InvalidArgIndex = UINT_MAX; 34 35 /// Check if the type has pointer size (very conservative). 36 inline bool isPointerSize(const Type *T) { 37 if (!T) 38 return true; 39 if (T->isIncompleteType()) 40 return true; 41 return (ASTC.getTypeSize(T) == PtrWidth); 42 } 43 44 /// Check if the type is a pointer/array to pointer sized values. 45 inline bool hasPointerToPointerSizedType(const Expr *E) { 46 QualType T = E->getType(); 47 48 // The type could be either a pointer or array. 49 const Type *TP = T.getTypePtr(); 50 QualType PointeeT = TP->getPointeeType(); 51 if (!PointeeT.isNull()) { 52 // If the type is a pointer to an array, check the size of the array 53 // elements. To avoid false positives coming from assumption that the 54 // values x and &x are equal when x is an array. 55 if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual()) 56 if (isPointerSize(TElem)) 57 return true; 58 59 // Else, check the pointee size. 60 return isPointerSize(PointeeT.getTypePtr()); 61 } 62 63 if (const Type *TElem = TP->getArrayElementTypeNoTypeQual()) 64 return isPointerSize(TElem); 65 66 // The type must be an array/pointer type. 67 68 // This could be a null constant, which is allowed. 69 if (E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull)) 70 return true; 71 return false; 72 } 73 74public: 75 WalkAST(BugReporter &br, AnalysisDeclContext* ac) 76 : BR(br), AC(ac), ASTC(AC->getASTContext()), 77 PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {} 78 79 // Statement visitor methods. 80 void VisitChildren(Stmt *S); 81 void VisitStmt(Stmt *S) { VisitChildren(S); } 82 void VisitCallExpr(CallExpr *CE); 83}; 84} // end anonymous namespace 85 86static StringRef getCalleeName(CallExpr *CE) { 87 const FunctionDecl *FD = CE->getDirectCallee(); 88 if (!FD) 89 return StringRef(); 90 91 IdentifierInfo *II = FD->getIdentifier(); 92 if (!II) // if no identifier, not a simple C function 93 return StringRef(); 94 95 return II->getName(); 96} 97 98void WalkAST::VisitCallExpr(CallExpr *CE) { 99 StringRef Name = getCalleeName(CE); 100 if (Name.empty()) 101 return; 102 103 const Expr *Arg = 0; 104 unsigned ArgNum = InvalidArgIndex; 105 106 if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) { 107 ArgNum = 1; 108 Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 109 if (hasPointerToPointerSizedType(Arg)) 110 return; 111 } 112 113 if (Arg == 0 && Name.equals("CFDictionaryCreate")) { 114 // Check first argument. 115 ArgNum = 1; 116 Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 117 if (hasPointerToPointerSizedType(Arg)) { 118 // Check second argument. 119 ArgNum = 2; 120 Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 121 if (hasPointerToPointerSizedType(Arg)) 122 // Both are good, return. 123 return; 124 } 125 } 126 127 if (ArgNum != InvalidArgIndex) { 128 assert(ArgNum == 1 || ArgNum == 2); 129 130 llvm::SmallString<256> BufName; 131 llvm::raw_svector_ostream OsName(BufName); 132 assert(ArgNum == 1 || ArgNum == 2); 133 OsName << " Invalid use of '" << Name << "'" ; 134 135 llvm::SmallString<256> Buf; 136 llvm::raw_svector_ostream Os(Buf); 137 Os << " The "<< ((ArgNum == 1) ? "first" : "second") << " argument to '" 138 << Name << "' must be a C array of pointer-sized values, not '" 139 << Arg->getType().getAsString() << "'"; 140 141 SourceRange R = Arg->getSourceRange(); 142 PathDiagnosticLocation CELoc = 143 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 144 BR.EmitBasicReport(OsName.str(), "Core Foundation/Objective-C", 145 Os.str(), CELoc, &R, 1); 146 } 147 148 // Recurse and check children. 149 VisitChildren(CE); 150} 151 152void WalkAST::VisitChildren(Stmt *S) { 153 for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) 154 if (Stmt *child = *I) 155 Visit(child); 156} 157 158namespace { 159class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> { 160public: 161 162 void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, 163 BugReporter &BR) const { 164 WalkAST walker(BR, Mgr.getAnalysisDeclContext(D)); 165 walker.Visit(D->getBody()); 166 } 167}; 168} 169 170void ento::registerObjCContainersASTChecker(CheckerManager &mgr) { 171 mgr.registerChecker<ObjCContainersASTChecker>(); 172} 173