aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
blob: d964a1668eaaabaf02062eef241a5334041f2c5f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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;
}