clang API Documentation
00001 //== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // An AST checker that looks for common pitfalls when using 'CFArray', 00011 // 'CFDictionary', 'CFSet' APIs. 00012 // 00013 //===----------------------------------------------------------------------===// 00014 #include "ClangSACheckers.h" 00015 #include "clang/AST/StmtVisitor.h" 00016 #include "clang/Analysis/AnalysisContext.h" 00017 #include "clang/Basic/TargetInfo.h" 00018 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 00019 #include "clang/StaticAnalyzer/Core/Checker.h" 00020 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 00021 #include "llvm/ADT/SmallString.h" 00022 #include "llvm/Support/raw_ostream.h" 00023 00024 using namespace clang; 00025 using namespace ento; 00026 00027 namespace { 00028 class WalkAST : public StmtVisitor<WalkAST> { 00029 BugReporter &BR; 00030 const CheckerBase *Checker; 00031 AnalysisDeclContext* AC; 00032 ASTContext &ASTC; 00033 uint64_t PtrWidth; 00034 00035 /// Check if the type has pointer size (very conservative). 00036 inline bool isPointerSize(const Type *T) { 00037 if (!T) 00038 return true; 00039 if (T->isIncompleteType()) 00040 return true; 00041 return (ASTC.getTypeSize(T) == PtrWidth); 00042 } 00043 00044 /// Check if the type is a pointer/array to pointer sized values. 00045 inline bool hasPointerToPointerSizedType(const Expr *E) { 00046 QualType T = E->getType(); 00047 00048 // The type could be either a pointer or array. 00049 const Type *TP = T.getTypePtr(); 00050 QualType PointeeT = TP->getPointeeType(); 00051 if (!PointeeT.isNull()) { 00052 // If the type is a pointer to an array, check the size of the array 00053 // elements. To avoid false positives coming from assumption that the 00054 // values x and &x are equal when x is an array. 00055 if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual()) 00056 if (isPointerSize(TElem)) 00057 return true; 00058 00059 // Else, check the pointee size. 00060 return isPointerSize(PointeeT.getTypePtr()); 00061 } 00062 00063 if (const Type *TElem = TP->getArrayElementTypeNoTypeQual()) 00064 return isPointerSize(TElem); 00065 00066 // The type must be an array/pointer type. 00067 00068 // This could be a null constant, which is allowed. 00069 if (E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull)) 00070 return true; 00071 return false; 00072 } 00073 00074 public: 00075 WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac) 00076 : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()), 00077 PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {} 00078 00079 // Statement visitor methods. 00080 void VisitChildren(Stmt *S); 00081 void VisitStmt(Stmt *S) { VisitChildren(S); } 00082 void VisitCallExpr(CallExpr *CE); 00083 }; 00084 } // end anonymous namespace 00085 00086 static StringRef getCalleeName(CallExpr *CE) { 00087 const FunctionDecl *FD = CE->getDirectCallee(); 00088 if (!FD) 00089 return StringRef(); 00090 00091 IdentifierInfo *II = FD->getIdentifier(); 00092 if (!II) // if no identifier, not a simple C function 00093 return StringRef(); 00094 00095 return II->getName(); 00096 } 00097 00098 void WalkAST::VisitCallExpr(CallExpr *CE) { 00099 StringRef Name = getCalleeName(CE); 00100 if (Name.empty()) 00101 return; 00102 00103 const Expr *Arg = nullptr; 00104 unsigned ArgNum; 00105 00106 if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) { 00107 if (CE->getNumArgs() != 4) 00108 return; 00109 ArgNum = 1; 00110 Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 00111 if (hasPointerToPointerSizedType(Arg)) 00112 return; 00113 } else if (Name.equals("CFDictionaryCreate")) { 00114 if (CE->getNumArgs() != 6) 00115 return; 00116 // Check first argument. 00117 ArgNum = 1; 00118 Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 00119 if (hasPointerToPointerSizedType(Arg)) { 00120 // Check second argument. 00121 ArgNum = 2; 00122 Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 00123 if (hasPointerToPointerSizedType(Arg)) 00124 // Both are good, return. 00125 return; 00126 } 00127 } 00128 00129 if (Arg) { 00130 assert(ArgNum == 1 || ArgNum == 2); 00131 00132 SmallString<64> BufName; 00133 llvm::raw_svector_ostream OsName(BufName); 00134 OsName << " Invalid use of '" << Name << "'" ; 00135 00136 SmallString<256> Buf; 00137 llvm::raw_svector_ostream Os(Buf); 00138 // Use "second" and "third" since users will expect 1-based indexing 00139 // for parameter names when mentioned in prose. 00140 Os << " The "<< ((ArgNum == 1) ? "second" : "third") << " argument to '" 00141 << Name << "' must be a C array of pointer-sized values, not '" 00142 << Arg->getType().getAsString() << "'"; 00143 00144 PathDiagnosticLocation CELoc = 00145 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 00146 BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(), 00147 categories::CoreFoundationObjectiveC, Os.str(), CELoc, 00148 Arg->getSourceRange()); 00149 } 00150 00151 // Recurse and check children. 00152 VisitChildren(CE); 00153 } 00154 00155 void WalkAST::VisitChildren(Stmt *S) { 00156 for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) 00157 if (Stmt *child = *I) 00158 Visit(child); 00159 } 00160 00161 namespace { 00162 class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> { 00163 public: 00164 00165 void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, 00166 BugReporter &BR) const { 00167 WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D)); 00168 walker.Visit(D->getBody()); 00169 } 00170 }; 00171 } 00172 00173 void ento::registerObjCContainersASTChecker(CheckerManager &mgr) { 00174 mgr.registerChecker<ObjCContainersASTChecker>(); 00175 }