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/Analysis/AnalysisContext.h" 00016 #include "clang/AST/StmtVisitor.h" 00017 #include "clang/Basic/TargetInfo.h" 00018 #include "clang/StaticAnalyzer/Core/Checker.h" 00019 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.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 AnalysisDeclContext* AC; 00031 ASTContext &ASTC; 00032 uint64_t PtrWidth; 00033 00034 static const unsigned InvalidArgIndex = UINT_MAX; 00035 00036 /// Check if the type has pointer size (very conservative). 00037 inline bool isPointerSize(const Type *T) { 00038 if (!T) 00039 return true; 00040 if (T->isIncompleteType()) 00041 return true; 00042 return (ASTC.getTypeSize(T) == PtrWidth); 00043 } 00044 00045 /// Check if the type is a pointer/array to pointer sized values. 00046 inline bool hasPointerToPointerSizedType(const Expr *E) { 00047 QualType T = E->getType(); 00048 00049 // The type could be either a pointer or array. 00050 const Type *TP = T.getTypePtr(); 00051 QualType PointeeT = TP->getPointeeType(); 00052 if (!PointeeT.isNull()) { 00053 // If the type is a pointer to an array, check the size of the array 00054 // elements. To avoid false positives coming from assumption that the 00055 // values x and &x are equal when x is an array. 00056 if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual()) 00057 if (isPointerSize(TElem)) 00058 return true; 00059 00060 // Else, check the pointee size. 00061 return isPointerSize(PointeeT.getTypePtr()); 00062 } 00063 00064 if (const Type *TElem = TP->getArrayElementTypeNoTypeQual()) 00065 return isPointerSize(TElem); 00066 00067 // The type must be an array/pointer type. 00068 00069 // This could be a null constant, which is allowed. 00070 if (E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull)) 00071 return true; 00072 return false; 00073 } 00074 00075 public: 00076 WalkAST(BugReporter &br, AnalysisDeclContext* ac) 00077 : BR(br), AC(ac), ASTC(AC->getASTContext()), 00078 PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {} 00079 00080 // Statement visitor methods. 00081 void VisitChildren(Stmt *S); 00082 void VisitStmt(Stmt *S) { VisitChildren(S); } 00083 void VisitCallExpr(CallExpr *CE); 00084 }; 00085 } // end anonymous namespace 00086 00087 static StringRef getCalleeName(CallExpr *CE) { 00088 const FunctionDecl *FD = CE->getDirectCallee(); 00089 if (!FD) 00090 return StringRef(); 00091 00092 IdentifierInfo *II = FD->getIdentifier(); 00093 if (!II) // if no identifier, not a simple C function 00094 return StringRef(); 00095 00096 return II->getName(); 00097 } 00098 00099 void WalkAST::VisitCallExpr(CallExpr *CE) { 00100 StringRef Name = getCalleeName(CE); 00101 if (Name.empty()) 00102 return; 00103 00104 const Expr *Arg = 0; 00105 unsigned ArgNum = InvalidArgIndex; 00106 00107 if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) { 00108 ArgNum = 1; 00109 Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 00110 if (hasPointerToPointerSizedType(Arg)) 00111 return; 00112 } 00113 00114 if (Arg == 0 && Name.equals("CFDictionaryCreate")) { 00115 // Check first argument. 00116 ArgNum = 1; 00117 Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 00118 if (hasPointerToPointerSizedType(Arg)) { 00119 // Check second argument. 00120 ArgNum = 2; 00121 Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); 00122 if (hasPointerToPointerSizedType(Arg)) 00123 // Both are good, return. 00124 return; 00125 } 00126 } 00127 00128 if (ArgNum != InvalidArgIndex) { 00129 assert(ArgNum == 1 || ArgNum == 2); 00130 00131 SmallString<256> BufName; 00132 llvm::raw_svector_ostream OsName(BufName); 00133 assert(ArgNum == 1 || ArgNum == 2); 00134 OsName << " Invalid use of '" << Name << "'" ; 00135 00136 SmallString<256> Buf; 00137 llvm::raw_svector_ostream Os(Buf); 00138 Os << " The "<< ((ArgNum == 1) ? "first" : "second") << " argument to '" 00139 << Name << "' must be a C array of pointer-sized values, not '" 00140 << Arg->getType().getAsString() << "'"; 00141 00142 SourceRange R = Arg->getSourceRange(); 00143 PathDiagnosticLocation CELoc = 00144 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 00145 BR.EmitBasicReport(AC->getDecl(), 00146 OsName.str(), categories::CoreFoundationObjectiveC, 00147 Os.str(), CELoc, &R, 1); 00148 } 00149 00150 // Recurse and check children. 00151 VisitChildren(CE); 00152 } 00153 00154 void WalkAST::VisitChildren(Stmt *S) { 00155 for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) 00156 if (Stmt *child = *I) 00157 Visit(child); 00158 } 00159 00160 namespace { 00161 class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> { 00162 public: 00163 00164 void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, 00165 BugReporter &BR) const { 00166 WalkAST walker(BR, Mgr.getAnalysisDeclContext(D)); 00167 walker.Visit(D->getBody()); 00168 } 00169 }; 00170 } 00171 00172 void ento::registerObjCContainersASTChecker(CheckerManager &mgr) { 00173 mgr.registerChecker<ObjCContainersASTChecker>(); 00174 }