Home | History | Annotate | Download | only in Checkers
      1 // MacOSXAPIChecker.h - Checks proper use of various MacOS X APIs --*- 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 // This defines MacOSXAPIChecker, which is an assortment of checks on calls
     11 // to various, widely used Apple APIs.
     12 //
     13 // FIXME: What's currently in BasicObjCFoundationChecks.cpp should be migrated
     14 // to here, using the new Checker interface.
     15 //
     16 //===----------------------------------------------------------------------===//
     17 
     18 #include "ClangSACheckers.h"
     19 #include "clang/Basic/TargetInfo.h"
     20 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
     21 #include "clang/StaticAnalyzer/Core/Checker.h"
     22 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
     23 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
     24 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
     25 #include "llvm/ADT/SmallString.h"
     26 #include "llvm/ADT/StringSwitch.h"
     27 #include "llvm/Support/raw_ostream.h"
     28 
     29 using namespace clang;
     30 using namespace ento;
     31 
     32 namespace {
     33 class MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > {
     34   mutable std::unique_ptr<BugType> BT_dispatchOnce;
     35 
     36 public:
     37   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
     38 
     39   void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
     40                          StringRef FName) const;
     41 
     42   typedef void (MacOSXAPIChecker::*SubChecker)(CheckerContext &,
     43                                                const CallExpr *,
     44                                                StringRef FName) const;
     45 };
     46 } //end anonymous namespace
     47 
     48 //===----------------------------------------------------------------------===//
     49 // dispatch_once and dispatch_once_f
     50 //===----------------------------------------------------------------------===//
     51 
     52 void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
     53                                          StringRef FName) const {
     54   if (CE->getNumArgs() < 1)
     55     return;
     56 
     57   // Check if the first argument is stack allocated.  If so, issue a warning
     58   // because that's likely to be bad news.
     59   ProgramStateRef state = C.getState();
     60   const MemRegion *R =
     61     state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion();
     62   if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
     63     return;
     64 
     65   ExplodedNode *N = C.generateErrorNode(state);
     66   if (!N)
     67     return;
     68 
     69   if (!BT_dispatchOnce)
     70     BT_dispatchOnce.reset(new BugType(this, "Improper use of 'dispatch_once'",
     71                                       "API Misuse (Apple)"));
     72 
     73   // Handle _dispatch_once.  In some versions of the OS X SDK we have the case
     74   // that dispatch_once is a macro that wraps a call to _dispatch_once.
     75   // _dispatch_once is then a function which then calls the real dispatch_once.
     76   // Users do not care; they just want the warning at the top-level call.
     77   if (CE->getLocStart().isMacroID()) {
     78     StringRef TrimmedFName = FName.ltrim('_');
     79     if (TrimmedFName != FName)
     80       FName = TrimmedFName;
     81   }
     82 
     83   SmallString<256> S;
     84   llvm::raw_svector_ostream os(S);
     85   os << "Call to '" << FName << "' uses";
     86   if (const VarRegion *VR = dyn_cast<VarRegion>(R))
     87     os << " the local variable '" << VR->getDecl()->getName() << '\'';
     88   else
     89     os << " stack allocated memory";
     90   os << " for the predicate value.  Using such transient memory for "
     91         "the predicate is potentially dangerous.";
     92   if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
     93     os << "  Perhaps you intended to declare the variable as 'static'?";
     94 
     95   auto report = llvm::make_unique<BugReport>(*BT_dispatchOnce, os.str(), N);
     96   report->addRange(CE->getArg(0)->getSourceRange());
     97   C.emitReport(std::move(report));
     98 }
     99 
    100 //===----------------------------------------------------------------------===//
    101 // Central dispatch function.
    102 //===----------------------------------------------------------------------===//
    103 
    104 void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE,
    105                                     CheckerContext &C) const {
    106   StringRef Name = C.getCalleeName(CE);
    107   if (Name.empty())
    108     return;
    109 
    110   SubChecker SC =
    111     llvm::StringSwitch<SubChecker>(Name)
    112       .Cases("dispatch_once",
    113              "_dispatch_once",
    114              "dispatch_once_f",
    115              &MacOSXAPIChecker::CheckDispatchOnce)
    116       .Default(nullptr);
    117 
    118   if (SC)
    119     (this->*SC)(C, CE, Name);
    120 }
    121 
    122 //===----------------------------------------------------------------------===//
    123 // Registration.
    124 //===----------------------------------------------------------------------===//
    125 
    126 void ento::registerMacOSXAPIChecker(CheckerManager &mgr) {
    127   mgr.registerChecker<MacOSXAPIChecker>();
    128 }
    129