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/AST/StmtVisitor.h" 16 #include "clang/Analysis/AnalysisContext.h" 17 #include "clang/Basic/TargetInfo.h" 18 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 19 #include "clang/StaticAnalyzer/Core/Checker.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 21 #include "llvm/ADT/SmallString.h" 22 #include "llvm/Support/raw_ostream.h" 23 24 using namespace clang; 25 using namespace ento; 26 27 namespace { 28 class WalkAST : public StmtVisitor<WalkAST> { 29 BugReporter &BR; 30 const CheckerBase *Checker; 31 AnalysisDeclContext* AC; 32 ASTContext &ASTC; 33 uint64_t PtrWidth; 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 74 public: 75 WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac) 76 : BR(br), Checker(checker), 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 86 static 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 98 void WalkAST::VisitCallExpr(CallExpr *CE) { 99 StringRef Name = getCalleeName(CE); 100 if (Name.empty()) 101 return; 102 103 const Expr *Arg = nullptr; 104 unsigned ArgNum; 105 106 if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) { 107 if (CE->getNumArgs() != 4) 108 return; 109 ArgNum = 1; 110 Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 111 if (hasPointerToPointerSizedType(Arg)) 112 return; 113 } else if (Name.equals("CFDictionaryCreate")) { 114 if (CE->getNumArgs() != 6) 115 return; 116 // Check first argument. 117 ArgNum = 1; 118 Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 119 if (hasPointerToPointerSizedType(Arg)) { 120 // Check second argument. 121 ArgNum = 2; 122 Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 123 if (hasPointerToPointerSizedType(Arg)) 124 // Both are good, return. 125 return; 126 } 127 } 128 129 if (Arg) { 130 assert(ArgNum == 1 || ArgNum == 2); 131 132 SmallString<64> BufName; 133 llvm::raw_svector_ostream OsName(BufName); 134 OsName << " Invalid use of '" << Name << "'" ; 135 136 SmallString<256> Buf; 137 llvm::raw_svector_ostream Os(Buf); 138 // Use "second" and "third" since users will expect 1-based indexing 139 // for parameter names when mentioned in prose. 140 Os << " The "<< ((ArgNum == 1) ? "second" : "third") << " argument to '" 141 << Name << "' must be a C array of pointer-sized values, not '" 142 << Arg->getType().getAsString() << "'"; 143 144 PathDiagnosticLocation CELoc = 145 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 146 BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(), 147 categories::CoreFoundationObjectiveC, Os.str(), CELoc, 148 Arg->getSourceRange()); 149 } 150 151 // Recurse and check children. 152 VisitChildren(CE); 153 } 154 155 void WalkAST::VisitChildren(Stmt *S) { 156 for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) 157 if (Stmt *child = *I) 158 Visit(child); 159 } 160 161 namespace { 162 class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> { 163 public: 164 165 void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, 166 BugReporter &BR) const { 167 WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D)); 168 walker.Visit(D->getBody()); 169 } 170 }; 171 } 172 173 void ento::registerObjCContainersASTChecker(CheckerManager &mgr) { 174 mgr.registerChecker<ObjCContainersASTChecker>(); 175 } 176