Clang Project

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