ObjCContainersASTChecker.cpp 5.5 KB

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