MacOSXAPIChecker.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // MacOSXAPIChecker.h - Checks proper use of various MacOS X APIs --*- 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. // This defines MacOSXAPIChecker, which is an assortment of checks on calls
  10. // to various, widely used Apple APIs.
  11. //
  12. // FIXME: What's currently in BasicObjCFoundationChecks.cpp should be migrated
  13. // to here, using the new Checker interface.
  14. //
  15. //===----------------------------------------------------------------------===//
  16. #include "clang/AST/Attr.h"
  17. #include "clang/Basic/TargetInfo.h"
  18. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  19. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  20. #include "clang/StaticAnalyzer/Core/Checker.h"
  21. #include "clang/StaticAnalyzer/Core/CheckerManager.h"
  22. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  23. #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
  24. #include "llvm/ADT/SmallString.h"
  25. #include "llvm/ADT/StringSwitch.h"
  26. #include "llvm/Support/raw_ostream.h"
  27. using namespace clang;
  28. using namespace ento;
  29. namespace {
  30. class MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > {
  31. mutable std::unique_ptr<BugType> BT_dispatchOnce;
  32. static const ObjCIvarRegion *getParentIvarRegion(const MemRegion *R);
  33. public:
  34. void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
  35. void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
  36. StringRef FName) const;
  37. typedef void (MacOSXAPIChecker::*SubChecker)(CheckerContext &,
  38. const CallExpr *,
  39. StringRef FName) const;
  40. };
  41. } //end anonymous namespace
  42. //===----------------------------------------------------------------------===//
  43. // dispatch_once and dispatch_once_f
  44. //===----------------------------------------------------------------------===//
  45. const ObjCIvarRegion *
  46. MacOSXAPIChecker::getParentIvarRegion(const MemRegion *R) {
  47. const SubRegion *SR = dyn_cast<SubRegion>(R);
  48. while (SR) {
  49. if (const ObjCIvarRegion *IR = dyn_cast<ObjCIvarRegion>(SR))
  50. return IR;
  51. SR = dyn_cast<SubRegion>(SR->getSuperRegion());
  52. }
  53. return nullptr;
  54. }
  55. void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
  56. StringRef FName) const {
  57. if (CE->getNumArgs() < 1)
  58. return;
  59. // Check if the first argument is improperly allocated. If so, issue a
  60. // warning because that's likely to be bad news.
  61. const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion();
  62. if (!R)
  63. return;
  64. // Global variables are fine.
  65. const MemRegion *RB = R->getBaseRegion();
  66. const MemSpaceRegion *RS = RB->getMemorySpace();
  67. if (isa<GlobalsSpaceRegion>(RS))
  68. return;
  69. // Handle _dispatch_once. In some versions of the OS X SDK we have the case
  70. // that dispatch_once is a macro that wraps a call to _dispatch_once.
  71. // _dispatch_once is then a function which then calls the real dispatch_once.
  72. // Users do not care; they just want the warning at the top-level call.
  73. if (CE->getBeginLoc().isMacroID()) {
  74. StringRef TrimmedFName = FName.ltrim('_');
  75. if (TrimmedFName != FName)
  76. FName = TrimmedFName;
  77. }
  78. SmallString<256> S;
  79. llvm::raw_svector_ostream os(S);
  80. bool SuggestStatic = false;
  81. os << "Call to '" << FName << "' uses";
  82. if (const VarRegion *VR = dyn_cast<VarRegion>(RB)) {
  83. const VarDecl *VD = VR->getDecl();
  84. // FIXME: These should have correct memory space and thus should be filtered
  85. // out earlier. This branch only fires when we're looking from a block,
  86. // which we analyze as a top-level declaration, onto a static local
  87. // in a function that contains the block.
  88. if (VD->isStaticLocal())
  89. return;
  90. // We filtered out globals earlier, so it must be a local variable
  91. // or a block variable which is under UnknownSpaceRegion.
  92. if (VR != R)
  93. os << " memory within";
  94. if (VD->hasAttr<BlocksAttr>())
  95. os << " the block variable '";
  96. else
  97. os << " the local variable '";
  98. os << VR->getDecl()->getName() << '\'';
  99. SuggestStatic = true;
  100. } else if (const ObjCIvarRegion *IVR = getParentIvarRegion(R)) {
  101. if (IVR != R)
  102. os << " memory within";
  103. os << " the instance variable '" << IVR->getDecl()->getName() << '\'';
  104. } else if (isa<HeapSpaceRegion>(RS)) {
  105. os << " heap-allocated memory";
  106. } else if (isa<UnknownSpaceRegion>(RS)) {
  107. // Presence of an IVar superregion has priority over this branch, because
  108. // ObjC objects are on the heap even if the core doesn't realize this.
  109. // Presence of a block variable base region has priority over this branch,
  110. // because block variables are known to be either on stack or on heap
  111. // (might actually move between the two, hence UnknownSpace).
  112. return;
  113. } else {
  114. os << " stack allocated memory";
  115. }
  116. os << " for the predicate value. Using such transient memory for "
  117. "the predicate is potentially dangerous.";
  118. if (SuggestStatic)
  119. os << " Perhaps you intended to declare the variable as 'static'?";
  120. ExplodedNode *N = C.generateErrorNode();
  121. if (!N)
  122. return;
  123. if (!BT_dispatchOnce)
  124. BT_dispatchOnce.reset(new BugType(this, "Improper use of 'dispatch_once'",
  125. "API Misuse (Apple)"));
  126. auto report =
  127. std::make_unique<PathSensitiveBugReport>(*BT_dispatchOnce, os.str(), N);
  128. report->addRange(CE->getArg(0)->getSourceRange());
  129. C.emitReport(std::move(report));
  130. }
  131. //===----------------------------------------------------------------------===//
  132. // Central dispatch function.
  133. //===----------------------------------------------------------------------===//
  134. void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE,
  135. CheckerContext &C) const {
  136. StringRef Name = C.getCalleeName(CE);
  137. if (Name.empty())
  138. return;
  139. SubChecker SC =
  140. llvm::StringSwitch<SubChecker>(Name)
  141. .Cases("dispatch_once",
  142. "_dispatch_once",
  143. "dispatch_once_f",
  144. &MacOSXAPIChecker::CheckDispatchOnce)
  145. .Default(nullptr);
  146. if (SC)
  147. (this->*SC)(C, CE, Name);
  148. }
  149. //===----------------------------------------------------------------------===//
  150. // Registration.
  151. //===----------------------------------------------------------------------===//
  152. void ento::registerMacOSXAPIChecker(CheckerManager &mgr) {
  153. mgr.registerChecker<MacOSXAPIChecker>();
  154. }
  155. bool ento::shouldRegisterMacOSXAPIChecker(const CheckerManager &mgr) {
  156. return true;
  157. }