diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp')
| -rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp | 180 | 
1 files changed, 180 insertions, 0 deletions
| diff --git a/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp new file mode 100644 index 000000000000..d964a1668eaa --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -0,0 +1,180 @@ +// MacOSXAPIChecker.h - Checks proper use of various MacOS X APIs --*- C++ -*-// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines MacOSXAPIChecker, which is an assortment of checks on calls +// to various, widely used Apple APIs. +// +// FIXME: What's currently in BasicObjCFoundationChecks.cpp should be migrated +// to here, using the new Checker interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > { +  mutable std::unique_ptr<BugType> BT_dispatchOnce; + +  static const ObjCIvarRegion *getParentIvarRegion(const MemRegion *R); + +public: +  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + +  void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, +                         StringRef FName) const; + +  typedef void (MacOSXAPIChecker::*SubChecker)(CheckerContext &, +                                               const CallExpr *, +                                               StringRef FName) const; +}; +} //end anonymous namespace + +//===----------------------------------------------------------------------===// +// dispatch_once and dispatch_once_f +//===----------------------------------------------------------------------===// + +const ObjCIvarRegion * +MacOSXAPIChecker::getParentIvarRegion(const MemRegion *R) { +  const SubRegion *SR = dyn_cast<SubRegion>(R); +  while (SR) { +    if (const ObjCIvarRegion *IR = dyn_cast<ObjCIvarRegion>(SR)) +      return IR; +    SR = dyn_cast<SubRegion>(SR->getSuperRegion()); +  } +  return nullptr; +} + +void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, +                                         StringRef FName) const { +  if (CE->getNumArgs() < 1) +    return; + +  // Check if the first argument is improperly allocated.  If so, issue a +  // warning because that's likely to be bad news. +  const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion(); +  if (!R) +    return; + +  // Global variables are fine. +  const MemRegion *RB = R->getBaseRegion(); +  const MemSpaceRegion *RS = RB->getMemorySpace(); +  if (isa<GlobalsSpaceRegion>(RS)) +    return; + +  // Handle _dispatch_once.  In some versions of the OS X SDK we have the case +  // that dispatch_once is a macro that wraps a call to _dispatch_once. +  // _dispatch_once is then a function which then calls the real dispatch_once. +  // Users do not care; they just want the warning at the top-level call. +  if (CE->getBeginLoc().isMacroID()) { +    StringRef TrimmedFName = FName.ltrim('_'); +    if (TrimmedFName != FName) +      FName = TrimmedFName; +  } + +  SmallString<256> S; +  llvm::raw_svector_ostream os(S); +  bool SuggestStatic = false; +  os << "Call to '" << FName << "' uses"; +  if (const VarRegion *VR = dyn_cast<VarRegion>(RB)) { +    const VarDecl *VD = VR->getDecl(); +    // FIXME: These should have correct memory space and thus should be filtered +    // out earlier. This branch only fires when we're looking from a block, +    // which we analyze as a top-level declaration, onto a static local +    // in a function that contains the block. +    if (VD->isStaticLocal()) +      return; +    // We filtered out globals earlier, so it must be a local variable +    // or a block variable which is under UnknownSpaceRegion. +    if (VR != R) +      os << " memory within"; +    if (VD->hasAttr<BlocksAttr>()) +      os << " the block variable '"; +    else +      os << " the local variable '"; +    os << VR->getDecl()->getName() << '\''; +    SuggestStatic = true; +  } else if (const ObjCIvarRegion *IVR = getParentIvarRegion(R)) { +    if (IVR != R) +      os << " memory within"; +    os << " the instance variable '" << IVR->getDecl()->getName() << '\''; +  } else if (isa<HeapSpaceRegion>(RS)) { +    os << " heap-allocated memory"; +  } else if (isa<UnknownSpaceRegion>(RS)) { +    // Presence of an IVar superregion has priority over this branch, because +    // ObjC objects are on the heap even if the core doesn't realize this. +    // Presence of a block variable base region has priority over this branch, +    // because block variables are known to be either on stack or on heap +    // (might actually move between the two, hence UnknownSpace). +    return; +  } else { +    os << " stack allocated memory"; +  } +  os << " for the predicate value.  Using such transient memory for " +        "the predicate is potentially dangerous."; +  if (SuggestStatic) +    os << "  Perhaps you intended to declare the variable as 'static'?"; + +  ExplodedNode *N = C.generateErrorNode(); +  if (!N) +    return; + +  if (!BT_dispatchOnce) +    BT_dispatchOnce.reset(new BugType(this, "Improper use of 'dispatch_once'", +                                      "API Misuse (Apple)")); + +  auto report = +      std::make_unique<PathSensitiveBugReport>(*BT_dispatchOnce, os.str(), N); +  report->addRange(CE->getArg(0)->getSourceRange()); +  C.emitReport(std::move(report)); +} + +//===----------------------------------------------------------------------===// +// Central dispatch function. +//===----------------------------------------------------------------------===// + +void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE, +                                    CheckerContext &C) const { +  StringRef Name = C.getCalleeName(CE); +  if (Name.empty()) +    return; + +  SubChecker SC = +    llvm::StringSwitch<SubChecker>(Name) +      .Cases("dispatch_once", +             "_dispatch_once", +             "dispatch_once_f", +             &MacOSXAPIChecker::CheckDispatchOnce) +      .Default(nullptr); + +  if (SC) +    (this->*SC)(C, CE, Name); +} + +//===----------------------------------------------------------------------===// +// Registration. +//===----------------------------------------------------------------------===// + +void ento::registerMacOSXAPIChecker(CheckerManager &mgr) { +  mgr.registerChecker<MacOSXAPIChecker>(); +} + +bool ento::shouldRegisterMacOSXAPIChecker(const LangOptions &LO) { +  return true; +} | 
