ObjCContainersASTChecker.cpp 5.5 KB

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