ObjCContainersASTChecker.cpp 5.4 KB

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