1218887Sdim// MacOSXAPIChecker.h - Checks proper use of various MacOS X APIs --*- C++ -*-//
2218887Sdim//
3218887Sdim//                     The LLVM Compiler Infrastructure
4218887Sdim//
5218887Sdim// This file is distributed under the University of Illinois Open Source
6218887Sdim// License. See LICENSE.TXT for details.
7218887Sdim//
8218887Sdim//===----------------------------------------------------------------------===//
9218887Sdim//
10218887Sdim// This defines MacOSXAPIChecker, which is an assortment of checks on calls
11249423Sdim// to various, widely used Apple APIs.
12218887Sdim//
13218887Sdim// FIXME: What's currently in BasicObjCFoundationChecks.cpp should be migrated
14218887Sdim// to here, using the new Checker interface.
15218887Sdim//
16218887Sdim//===----------------------------------------------------------------------===//
17218887Sdim
18218887Sdim#include "ClangSACheckers.h"
19249423Sdim#include "clang/Basic/TargetInfo.h"
20249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
22218887Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h"
23219077Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24226633Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
25218887Sdim#include "llvm/ADT/SmallString.h"
26218887Sdim#include "llvm/ADT/StringSwitch.h"
27218887Sdim#include "llvm/Support/raw_ostream.h"
28218887Sdim
29218887Sdimusing namespace clang;
30218887Sdimusing namespace ento;
31218887Sdim
32218887Sdimnamespace {
33221345Sdimclass MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > {
34234353Sdim  mutable OwningPtr<BugType> BT_dispatchOnce;
35218887Sdim
36218887Sdimpublic:
37224145Sdim  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
38218887Sdim
39224145Sdim  void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
40234353Sdim                         StringRef FName) const;
41224145Sdim
42224145Sdim  typedef void (MacOSXAPIChecker::*SubChecker)(CheckerContext &,
43224145Sdim                                               const CallExpr *,
44234353Sdim                                               StringRef FName) const;
45218887Sdim};
46218887Sdim} //end anonymous namespace
47218887Sdim
48218887Sdim//===----------------------------------------------------------------------===//
49218887Sdim// dispatch_once and dispatch_once_f
50218887Sdim//===----------------------------------------------------------------------===//
51218887Sdim
52224145Sdimvoid MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
53234353Sdim                                         StringRef FName) const {
54218887Sdim  if (CE->getNumArgs() < 1)
55218887Sdim    return;
56218887Sdim
57218887Sdim  // Check if the first argument is stack allocated.  If so, issue a warning
58218887Sdim  // because that's likely to be bad news.
59234353Sdim  ProgramStateRef state = C.getState();
60234353Sdim  const MemRegion *R =
61234353Sdim    state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion();
62218887Sdim  if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
63218887Sdim    return;
64218887Sdim
65218887Sdim  ExplodedNode *N = C.generateSink(state);
66218887Sdim  if (!N)
67218887Sdim    return;
68218887Sdim
69224145Sdim  if (!BT_dispatchOnce)
70224145Sdim    BT_dispatchOnce.reset(new BugType("Improper use of 'dispatch_once'",
71249423Sdim                                      "API Misuse (Apple)"));
72224145Sdim
73243830Sdim  // Handle _dispatch_once.  In some versions of the OS X SDK we have the case
74243830Sdim  // that dispatch_once is a macro that wraps a call to _dispatch_once.
75243830Sdim  // _dispatch_once is then a function which then calls the real dispatch_once.
76243830Sdim  // Users do not care; they just want the warning at the top-level call.
77243830Sdim  if (CE->getLocStart().isMacroID()) {
78243830Sdim    StringRef TrimmedFName = FName.ltrim("_");
79243830Sdim    if (TrimmedFName != FName)
80243830Sdim      FName = TrimmedFName;
81243830Sdim  }
82243830Sdim
83234353Sdim  SmallString<256> S;
84218887Sdim  llvm::raw_svector_ostream os(S);
85234353Sdim  os << "Call to '" << FName << "' uses";
86218887Sdim  if (const VarRegion *VR = dyn_cast<VarRegion>(R))
87218887Sdim    os << " the local variable '" << VR->getDecl()->getName() << '\'';
88218887Sdim  else
89218887Sdim    os << " stack allocated memory";
90218887Sdim  os << " for the predicate value.  Using such transient memory for "
91218887Sdim        "the predicate is potentially dangerous.";
92218887Sdim  if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
93218887Sdim    os << "  Perhaps you intended to declare the variable as 'static'?";
94218887Sdim
95226633Sdim  BugReport *report = new BugReport(*BT_dispatchOnce, os.str(), N);
96218887Sdim  report->addRange(CE->getArg(0)->getSourceRange());
97243830Sdim  C.emitReport(report);
98218887Sdim}
99218887Sdim
100218887Sdim//===----------------------------------------------------------------------===//
101218887Sdim// Central dispatch function.
102218887Sdim//===----------------------------------------------------------------------===//
103218887Sdim
104219077Sdimvoid MacOSXAPIChecker::checkPreStmt(const CallExpr *CE,
105219077Sdim                                    CheckerContext &C) const {
106234353Sdim  StringRef Name = C.getCalleeName(CE);
107234353Sdim  if (Name.empty())
108218887Sdim    return;
109218887Sdim
110224145Sdim  SubChecker SC =
111234353Sdim    llvm::StringSwitch<SubChecker>(Name)
112243830Sdim      .Cases("dispatch_once",
113243830Sdim             "_dispatch_once",
114243830Sdim             "dispatch_once_f",
115224145Sdim             &MacOSXAPIChecker::CheckDispatchOnce)
116224145Sdim      .Default(NULL);
117218887Sdim
118224145Sdim  if (SC)
119234353Sdim    (this->*SC)(C, CE, Name);
120218887Sdim}
121219077Sdim
122219077Sdim//===----------------------------------------------------------------------===//
123219077Sdim// Registration.
124219077Sdim//===----------------------------------------------------------------------===//
125219077Sdim
126219077Sdimvoid ento::registerMacOSXAPIChecker(CheckerManager &mgr) {
127219077Sdim  mgr.registerChecker<MacOSXAPIChecker>();
128219077Sdim}
129