ObjCContainersASTChecker.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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/Analysis/AnalysisContext.h"
  16. #include "clang/AST/StmtVisitor.h"
  17. #include "clang/Basic/TargetInfo.h"
  18. #include "clang/StaticAnalyzer/Core/Checker.h"
  19. #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.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. AnalysisDeclContext* AC;
  29. ASTContext &ASTC;
  30. uint64_t PtrWidth;
  31. static const unsigned InvalidArgIndex = UINT_MAX;
  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, AnalysisDeclContext* ac)
  66. : BR(br), 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 = 0;
  88. unsigned ArgNum = InvalidArgIndex;
  89. if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) {
  90. ArgNum = 1;
  91. Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
  92. if (hasPointerToPointerSizedType(Arg))
  93. return;
  94. }
  95. if (Arg == 0 && Name.equals("CFDictionaryCreate")) {
  96. // Check first argument.
  97. ArgNum = 1;
  98. Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
  99. if (hasPointerToPointerSizedType(Arg)) {
  100. // Check second argument.
  101. ArgNum = 2;
  102. Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
  103. if (hasPointerToPointerSizedType(Arg))
  104. // Both are good, return.
  105. return;
  106. }
  107. }
  108. if (ArgNum != InvalidArgIndex) {
  109. assert(ArgNum == 1 || ArgNum == 2);
  110. SmallString<256> BufName;
  111. llvm::raw_svector_ostream OsName(BufName);
  112. assert(ArgNum == 1 || ArgNum == 2);
  113. OsName << " Invalid use of '" << Name << "'" ;
  114. SmallString<256> Buf;
  115. llvm::raw_svector_ostream Os(Buf);
  116. Os << " The "<< ((ArgNum == 1) ? "first" : "second") << " argument to '"
  117. << Name << "' must be a C array of pointer-sized values, not '"
  118. << Arg->getType().getAsString() << "'";
  119. SourceRange R = Arg->getSourceRange();
  120. PathDiagnosticLocation CELoc =
  121. PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
  122. BR.EmitBasicReport(OsName.str(), "Core Foundation/Objective-C",
  123. Os.str(), CELoc, &R, 1);
  124. }
  125. // Recurse and check children.
  126. VisitChildren(CE);
  127. }
  128. void WalkAST::VisitChildren(Stmt *S) {
  129. for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I)
  130. if (Stmt *child = *I)
  131. Visit(child);
  132. }
  133. namespace {
  134. class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> {
  135. public:
  136. void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
  137. BugReporter &BR) const {
  138. WalkAST walker(BR, Mgr.getAnalysisDeclContext(D));
  139. walker.Visit(D->getBody());
  140. }
  141. };
  142. }
  143. void ento::registerObjCContainersASTChecker(CheckerManager &mgr) {
  144. mgr.registerChecker<ObjCContainersASTChecker>();
  145. }