blob: 437378e53daa054fab227953322b207329154a75 [file] [log] [blame]
Ted Kremenekb663ffe2010-02-25 05:44:09 +00001// 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
Anna Zaks8ef07e52013-04-03 19:28:22 +000011// to various, widely used Apple APIs.
Ted Kremenekb663ffe2010-02-25 05:44:09 +000012//
13// FIXME: What's currently in BasicObjCFoundationChecks.cpp should be migrated
14// to here, using the new Checker interface.
15//
16//===----------------------------------------------------------------------===//
17
Argyrios Kyrtzidisa6d04d52011-02-15 07:42:33 +000018#include "ClangSACheckers.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000019#include "clang/Basic/TargetInfo.h"
20#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +000021#include "clang/StaticAnalyzer/Core/Checker.h"
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +000022#include "clang/StaticAnalyzer/Core/CheckerManager.h"
Argyrios Kyrtzidisdff865d2011-02-23 01:05:36 +000023#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
Ted Kremenek001fd5b2011-08-15 22:09:50 +000024#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
Ted Kremenekb663ffe2010-02-25 05:44:09 +000025#include "llvm/ADT/SmallString.h"
26#include "llvm/ADT/StringSwitch.h"
27#include "llvm/Support/raw_ostream.h"
28
29using namespace clang;
Ted Kremenek98857c92010-12-23 07:20:52 +000030using namespace ento;
Ted Kremenekb663ffe2010-02-25 05:44:09 +000031
32namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +000033class MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > {
Ahmed Charlesb8984322014-03-07 20:03:18 +000034 mutable std::unique_ptr<BugType> BT_dispatchOnce;
Ted Kremenekb663ffe2010-02-25 05:44:09 +000035
Artem Dergachevaacc03c2016-10-31 17:27:26 +000036 static const ObjCIvarRegion *getParentIvarRegion(const MemRegion *R);
37
Ted Kremenekb663ffe2010-02-25 05:44:09 +000038public:
Argyrios Kyrtzidisdff865d2011-02-23 01:05:36 +000039 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
Jordy Roseede26952011-07-15 06:02:19 +000040
41 void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
Anna Zaksc6aa5312011-12-01 05:57:37 +000042 StringRef FName) const;
Jordy Roseede26952011-07-15 06:02:19 +000043
44 typedef void (MacOSXAPIChecker::*SubChecker)(CheckerContext &,
45 const CallExpr *,
Anna Zaksc6aa5312011-12-01 05:57:37 +000046 StringRef FName) const;
Ted Kremenekb663ffe2010-02-25 05:44:09 +000047};
48} //end anonymous namespace
49
Ted Kremenekb663ffe2010-02-25 05:44:09 +000050//===----------------------------------------------------------------------===//
51// dispatch_once and dispatch_once_f
52//===----------------------------------------------------------------------===//
53
Artem Dergachevaacc03c2016-10-31 17:27:26 +000054const ObjCIvarRegion *
55MacOSXAPIChecker::getParentIvarRegion(const MemRegion *R) {
56 const SubRegion *SR = dyn_cast<SubRegion>(R);
57 while (SR) {
58 if (const ObjCIvarRegion *IR = dyn_cast<ObjCIvarRegion>(SR))
59 return IR;
60 SR = dyn_cast<SubRegion>(SR->getSuperRegion());
61 }
62 return nullptr;
63}
64
Jordy Roseede26952011-07-15 06:02:19 +000065void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
Anna Zaksc6aa5312011-12-01 05:57:37 +000066 StringRef FName) const {
Ted Kremenekb663ffe2010-02-25 05:44:09 +000067 if (CE->getNumArgs() < 1)
68 return;
69
Artem Dergachevaacc03c2016-10-31 17:27:26 +000070 // Check if the first argument is improperly allocated. If so, issue a
71 // warning because that's likely to be bad news.
72 const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion();
73 if (!R)
Ted Kremenekb663ffe2010-02-25 05:44:09 +000074 return;
75
Artem Dergachevaacc03c2016-10-31 17:27:26 +000076 // Global variables are fine.
77 const MemRegion *RB = R->getBaseRegion();
78 const MemSpaceRegion *RS = RB->getMemorySpace();
79 if (isa<GlobalsSpaceRegion>(RS))
Ted Kremenekb663ffe2010-02-25 05:44:09 +000080 return;
81
Ted Kremenek5371c732012-09-13 19:48:51 +000082 // Handle _dispatch_once. In some versions of the OS X SDK we have the case
83 // that dispatch_once is a macro that wraps a call to _dispatch_once.
84 // _dispatch_once is then a function which then calls the real dispatch_once.
85 // Users do not care; they just want the warning at the top-level call.
Ted Kremeneke5c0a9b2012-09-13 18:18:37 +000086 if (CE->getLocStart().isMacroID()) {
Vedant Kumar409506e2016-02-16 02:14:44 +000087 StringRef TrimmedFName = FName.ltrim('_');
Ted Kremeneke5c0a9b2012-09-13 18:18:37 +000088 if (TrimmedFName != FName)
89 FName = TrimmedFName;
90 }
Ted Kremenek3a0678e2015-09-08 03:50:52 +000091
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +000092 SmallString<256> S;
Ted Kremenekb663ffe2010-02-25 05:44:09 +000093 llvm::raw_svector_ostream os(S);
Artem Dergachevaacc03c2016-10-31 17:27:26 +000094 bool SuggestStatic = false;
Anna Zaksc6aa5312011-12-01 05:57:37 +000095 os << "Call to '" << FName << "' uses";
Artem Dergachevaacc03c2016-10-31 17:27:26 +000096 if (const VarRegion *VR = dyn_cast<VarRegion>(RB)) {
Artem Dergachev55705952017-01-25 10:21:45 +000097 const VarDecl *VD = VR->getDecl();
98 // FIXME: These should have correct memory space and thus should be filtered
99 // out earlier. This branch only fires when we're looking from a block,
100 // which we analyze as a top-level declaration, onto a static local
101 // in a function that contains the block.
102 if (VD->isStaticLocal())
103 return;
Artem Dergacheva21df232016-10-31 21:04:54 +0000104 // We filtered out globals earlier, so it must be a local variable
105 // or a block variable which is under UnknownSpaceRegion.
Artem Dergachevaacc03c2016-10-31 17:27:26 +0000106 if (VR != R)
107 os << " memory within";
Artem Dergachev55705952017-01-25 10:21:45 +0000108 if (VD->hasAttr<BlocksAttr>())
Artem Dergacheva21df232016-10-31 21:04:54 +0000109 os << " the block variable '";
110 else
111 os << " the local variable '";
112 os << VR->getDecl()->getName() << '\'';
Artem Dergachevaacc03c2016-10-31 17:27:26 +0000113 SuggestStatic = true;
114 } else if (const ObjCIvarRegion *IVR = getParentIvarRegion(R)) {
115 if (IVR != R)
116 os << " memory within";
117 os << " the instance variable '" << IVR->getDecl()->getName() << '\'';
118 } else if (isa<HeapSpaceRegion>(RS)) {
119 os << " heap-allocated memory";
120 } else if (isa<UnknownSpaceRegion>(RS)) {
121 // Presence of an IVar superregion has priority over this branch, because
122 // ObjC objects are on the heap even if the core doesn't realize this.
Artem Dergacheva21df232016-10-31 21:04:54 +0000123 // Presence of a block variable base region has priority over this branch,
124 // because block variables are known to be either on stack or on heap
125 // (might actually move between the two, hence UnknownSpace).
Artem Dergachevaacc03c2016-10-31 17:27:26 +0000126 return;
127 } else {
Ted Kremenekb663ffe2010-02-25 05:44:09 +0000128 os << " stack allocated memory";
Artem Dergachevaacc03c2016-10-31 17:27:26 +0000129 }
Ted Kremenekb663ffe2010-02-25 05:44:09 +0000130 os << " for the predicate value. Using such transient memory for "
131 "the predicate is potentially dangerous.";
Artem Dergachevaacc03c2016-10-31 17:27:26 +0000132 if (SuggestStatic)
Ted Kremenekb663ffe2010-02-25 05:44:09 +0000133 os << " Perhaps you intended to declare the variable as 'static'?";
134
Artem Dergachevaacc03c2016-10-31 17:27:26 +0000135 ExplodedNode *N = C.generateErrorNode();
136 if (!N)
137 return;
138
139 if (!BT_dispatchOnce)
140 BT_dispatchOnce.reset(new BugType(this, "Improper use of 'dispatch_once'",
141 "API Misuse (Apple)"));
142
Aaron Ballman8d3a7a52015-06-23 13:15:32 +0000143 auto report = llvm::make_unique<BugReport>(*BT_dispatchOnce, os.str(), N);
Ted Kremenekb663ffe2010-02-25 05:44:09 +0000144 report->addRange(CE->getArg(0)->getSourceRange());
Aaron Ballman8d3a7a52015-06-23 13:15:32 +0000145 C.emitReport(std::move(report));
Ted Kremenekb663ffe2010-02-25 05:44:09 +0000146}
147
148//===----------------------------------------------------------------------===//
149// Central dispatch function.
150//===----------------------------------------------------------------------===//
151
Argyrios Kyrtzidisdff865d2011-02-23 01:05:36 +0000152void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE,
153 CheckerContext &C) const {
Anna Zaksc6aa5312011-12-01 05:57:37 +0000154 StringRef Name = C.getCalleeName(CE);
155 if (Name.empty())
Ted Kremenekb663ffe2010-02-25 05:44:09 +0000156 return;
157
Jordy Roseede26952011-07-15 06:02:19 +0000158 SubChecker SC =
Anna Zaksc6aa5312011-12-01 05:57:37 +0000159 llvm::StringSwitch<SubChecker>(Name)
Ted Kremeneke5c0a9b2012-09-13 18:18:37 +0000160 .Cases("dispatch_once",
161 "_dispatch_once",
162 "dispatch_once_f",
Jordy Roseede26952011-07-15 06:02:19 +0000163 &MacOSXAPIChecker::CheckDispatchOnce)
Craig Topper0dbb7832014-05-27 02:45:47 +0000164 .Default(nullptr);
Ted Kremenekb663ffe2010-02-25 05:44:09 +0000165
Jordy Roseede26952011-07-15 06:02:19 +0000166 if (SC)
Anna Zaksc6aa5312011-12-01 05:57:37 +0000167 (this->*SC)(C, CE, Name);
Ted Kremenekb663ffe2010-02-25 05:44:09 +0000168}
Argyrios Kyrtzidisdff865d2011-02-23 01:05:36 +0000169
170//===----------------------------------------------------------------------===//
171// Registration.
172//===----------------------------------------------------------------------===//
173
174void ento::registerMacOSXAPIChecker(CheckerManager &mgr) {
175 mgr.registerChecker<MacOSXAPIChecker>();
176}