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