2010-02-25 13:44:09 +08:00
|
|
|
// MacOSXAPIChecker.h - Checks proper use of various MacOS X APIs --*- C++ -*-//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// 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
|
2010-02-25 13:44:09 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This defines MacOSXAPIChecker, which is an assortment of checks on calls
|
2013-04-04 03:28:22 +08:00
|
|
|
// to various, widely used Apple APIs.
|
2010-02-25 13:44:09 +08:00
|
|
|
//
|
|
|
|
// FIXME: What's currently in BasicObjCFoundationChecks.cpp should be migrated
|
|
|
|
// to here, using the new Checker interface.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
[analyzer][NFC] Move CheckerRegistry from the Core directory to Frontend
ClangCheckerRegistry is a very non-obvious, poorly documented, weird concept.
It derives from CheckerRegistry, and is placed in lib/StaticAnalyzer/Frontend,
whereas it's base is located in lib/StaticAnalyzer/Core. It was, from what I can
imagine, used to circumvent the problem that the registry functions of the
checkers are located in the clangStaticAnalyzerCheckers library, but that
library depends on clangStaticAnalyzerCore. However, clangStaticAnalyzerFrontend
depends on both of those libraries.
One can make the observation however, that CheckerRegistry has no place in Core,
it isn't used there at all! The only place where it is used is Frontend, which
is where it ultimately belongs.
This move implies that since
include/clang/StaticAnalyzer/Checkers/ClangCheckers.h only contained a single function:
class CheckerRegistry;
void registerBuiltinCheckers(CheckerRegistry ®istry);
it had to re purposed, as CheckerRegistry is no longer available to
clangStaticAnalyzerCheckers. It was renamed to BuiltinCheckerRegistration.h,
which actually describes it a lot better -- it does not contain the registration
functions for checkers, but only those generated by the tblgen files.
Differential Revision: https://reviews.llvm.org/D54436
llvm-svn: 349275
2018-12-16 00:23:51 +08:00
|
|
|
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/Basic/TargetInfo.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
2011-03-01 09:16:21 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
2011-02-18 05:39:17 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
2011-02-23 09:05:36 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
2011-08-16 06:09:50 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
|
2010-02-25 13:44:09 +08:00
|
|
|
#include "llvm/ADT/SmallString.h"
|
|
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
|
|
|
|
using namespace clang;
|
2010-12-23 15:20:52 +08:00
|
|
|
using namespace ento;
|
2010-02-25 13:44:09 +08:00
|
|
|
|
|
|
|
namespace {
|
2011-03-01 09:16:21 +08:00
|
|
|
class MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > {
|
2014-03-08 04:03:18 +08:00
|
|
|
mutable std::unique_ptr<BugType> BT_dispatchOnce;
|
2010-02-25 13:44:09 +08:00
|
|
|
|
2016-11-01 01:27:26 +08:00
|
|
|
static const ObjCIvarRegion *getParentIvarRegion(const MemRegion *R);
|
|
|
|
|
2010-02-25 13:44:09 +08:00
|
|
|
public:
|
2011-02-23 09:05:36 +08:00
|
|
|
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
|
2011-07-15 14:02:19 +08:00
|
|
|
|
|
|
|
void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
|
2011-12-01 13:57:37 +08:00
|
|
|
StringRef FName) const;
|
2011-07-15 14:02:19 +08:00
|
|
|
|
|
|
|
typedef void (MacOSXAPIChecker::*SubChecker)(CheckerContext &,
|
|
|
|
const CallExpr *,
|
2011-12-01 13:57:37 +08:00
|
|
|
StringRef FName) const;
|
2010-02-25 13:44:09 +08:00
|
|
|
};
|
|
|
|
} //end anonymous namespace
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// dispatch_once and dispatch_once_f
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2016-11-01 01:27:26 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-07-15 14:02:19 +08:00
|
|
|
void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
|
2011-12-01 13:57:37 +08:00
|
|
|
StringRef FName) const {
|
2010-02-25 13:44:09 +08:00
|
|
|
if (CE->getNumArgs() < 1)
|
|
|
|
return;
|
|
|
|
|
2016-11-01 01:27:26 +08:00
|
|
|
// 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)
|
2010-02-25 13:44:09 +08:00
|
|
|
return;
|
|
|
|
|
2016-11-01 01:27:26 +08:00
|
|
|
// Global variables are fine.
|
|
|
|
const MemRegion *RB = R->getBaseRegion();
|
|
|
|
const MemSpaceRegion *RS = RB->getMemorySpace();
|
|
|
|
if (isa<GlobalsSpaceRegion>(RS))
|
2010-02-25 13:44:09 +08:00
|
|
|
return;
|
|
|
|
|
2012-09-14 03:48:51 +08:00
|
|
|
// 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.
|
2018-08-10 05:08:08 +08:00
|
|
|
if (CE->getBeginLoc().isMacroID()) {
|
2016-02-16 10:14:44 +08:00
|
|
|
StringRef TrimmedFName = FName.ltrim('_');
|
2012-09-14 02:18:37 +08:00
|
|
|
if (TrimmedFName != FName)
|
|
|
|
FName = TrimmedFName;
|
|
|
|
}
|
2015-09-08 11:50:52 +08:00
|
|
|
|
2012-02-05 10:13:05 +08:00
|
|
|
SmallString<256> S;
|
2010-02-25 13:44:09 +08:00
|
|
|
llvm::raw_svector_ostream os(S);
|
2016-11-01 01:27:26 +08:00
|
|
|
bool SuggestStatic = false;
|
2011-12-01 13:57:37 +08:00
|
|
|
os << "Call to '" << FName << "' uses";
|
2016-11-01 01:27:26 +08:00
|
|
|
if (const VarRegion *VR = dyn_cast<VarRegion>(RB)) {
|
2017-01-25 18:21:45 +08:00
|
|
|
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;
|
2016-11-01 05:04:54 +08:00
|
|
|
// We filtered out globals earlier, so it must be a local variable
|
|
|
|
// or a block variable which is under UnknownSpaceRegion.
|
2016-11-01 01:27:26 +08:00
|
|
|
if (VR != R)
|
|
|
|
os << " memory within";
|
2017-01-25 18:21:45 +08:00
|
|
|
if (VD->hasAttr<BlocksAttr>())
|
2016-11-01 05:04:54 +08:00
|
|
|
os << " the block variable '";
|
|
|
|
else
|
|
|
|
os << " the local variable '";
|
|
|
|
os << VR->getDecl()->getName() << '\'';
|
2016-11-01 01:27:26 +08:00
|
|
|
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.
|
2016-11-01 05:04:54 +08:00
|
|
|
// 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).
|
2016-11-01 01:27:26 +08:00
|
|
|
return;
|
|
|
|
} else {
|
2010-02-25 13:44:09 +08:00
|
|
|
os << " stack allocated memory";
|
2016-11-01 01:27:26 +08:00
|
|
|
}
|
2010-02-25 13:44:09 +08:00
|
|
|
os << " for the predicate value. Using such transient memory for "
|
|
|
|
"the predicate is potentially dangerous.";
|
2016-11-01 01:27:26 +08:00
|
|
|
if (SuggestStatic)
|
2010-02-25 13:44:09 +08:00
|
|
|
os << " Perhaps you intended to declare the variable as 'static'?";
|
|
|
|
|
2016-11-01 01:27:26 +08:00
|
|
|
ExplodedNode *N = C.generateErrorNode();
|
|
|
|
if (!N)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!BT_dispatchOnce)
|
|
|
|
BT_dispatchOnce.reset(new BugType(this, "Improper use of 'dispatch_once'",
|
|
|
|
"API Misuse (Apple)"));
|
|
|
|
|
2015-06-23 21:15:32 +08:00
|
|
|
auto report = llvm::make_unique<BugReport>(*BT_dispatchOnce, os.str(), N);
|
2010-02-25 13:44:09 +08:00
|
|
|
report->addRange(CE->getArg(0)->getSourceRange());
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(std::move(report));
|
2010-02-25 13:44:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Central dispatch function.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2011-02-23 09:05:36 +08:00
|
|
|
void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE,
|
|
|
|
CheckerContext &C) const {
|
2011-12-01 13:57:37 +08:00
|
|
|
StringRef Name = C.getCalleeName(CE);
|
|
|
|
if (Name.empty())
|
2010-02-25 13:44:09 +08:00
|
|
|
return;
|
|
|
|
|
2011-07-15 14:02:19 +08:00
|
|
|
SubChecker SC =
|
2011-12-01 13:57:37 +08:00
|
|
|
llvm::StringSwitch<SubChecker>(Name)
|
2012-09-14 02:18:37 +08:00
|
|
|
.Cases("dispatch_once",
|
|
|
|
"_dispatch_once",
|
|
|
|
"dispatch_once_f",
|
2011-07-15 14:02:19 +08:00
|
|
|
&MacOSXAPIChecker::CheckDispatchOnce)
|
2014-05-27 10:45:47 +08:00
|
|
|
.Default(nullptr);
|
2010-02-25 13:44:09 +08:00
|
|
|
|
2011-07-15 14:02:19 +08:00
|
|
|
if (SC)
|
2011-12-01 13:57:37 +08:00
|
|
|
(this->*SC)(C, CE, Name);
|
2010-02-25 13:44:09 +08:00
|
|
|
}
|
2011-02-23 09:05:36 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Registration.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
void ento::registerMacOSXAPIChecker(CheckerManager &mgr) {
|
|
|
|
mgr.registerChecker<MacOSXAPIChecker>();
|
|
|
|
}
|
2019-01-26 22:23:08 +08:00
|
|
|
|
|
|
|
bool ento::shouldRegisterMacOSXAPIChecker(const LangOptions &LO) {
|
|
|
|
return true;
|
|
|
|
}
|