aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/StaticAnalyzer
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/StaticAnalyzer')
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp38
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp146
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp30
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp188
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp20
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp98
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp79
7 files changed, 216 insertions, 383 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index 88feb6a..e682c4e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -99,7 +99,7 @@ class NilArgChecker : public Checker<check::PreObjCMessage,
check::PostStmt<ObjCDictionaryLiteral>,
check::PostStmt<ObjCArrayLiteral>,
EventDispatcher<ImplicitNullDerefEvent>> {
- mutable std::unique_ptr<APIMisuse> BT;
+ const APIMisuse BT{this, "nil argument"};
mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors;
mutable Selector ArrayWithObjectSel;
@@ -218,10 +218,7 @@ void NilArgChecker::generateBugReport(ExplodedNode *N,
SourceRange Range,
const Expr *E,
CheckerContext &C) const {
- if (!BT)
- BT.reset(new APIMisuse(this, "nil argument"));
-
- auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
R->addRange(Range);
bugreporter::trackExpressionValue(N, E, *R);
C.emitReport(std::move(R));
@@ -350,7 +347,7 @@ void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
namespace {
class CFNumberChecker : public Checker< check::PreStmt<CallExpr> > {
- mutable std::unique_ptr<APIMisuse> BT;
+ const APIMisuse BT{this, "Bad use of CFNumber APIs"};
mutable IdentifierInfo *ICreate = nullptr, *IGetValue = nullptr;
public:
CFNumberChecker() = default;
@@ -524,10 +521,7 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE,
<< " bits of the integer value will be "
<< (isCreate ? "lost." : "garbage.");
- if (!BT)
- BT.reset(new APIMisuse(this, "Bad use of CFNumber APIs"));
-
- auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
+ auto report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
report->addRange(CE->getArg(2)->getSourceRange());
C.emitReport(std::move(report));
}
@@ -539,7 +533,7 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE,
namespace {
class CFRetainReleaseChecker : public Checker<check::PreCall> {
- mutable APIMisuse BT{this, "null passed to CF memory management function"};
+ const APIMisuse BT{this, "null passed to CF memory management function"};
const CallDescriptionSet ModelledCalls = {
{CDM::CLibrary, {"CFRetain"}, 1},
{CDM::CLibrary, {"CFRelease"}, 1},
@@ -600,7 +594,8 @@ class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
mutable Selector retainS;
mutable Selector autoreleaseS;
mutable Selector drainS;
- mutable std::unique_ptr<BugType> BT;
+ const APIMisuse BT{
+ this, "message incorrectly sent to class instead of class instance"};
public:
void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
@@ -609,10 +604,7 @@ public:
void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
CheckerContext &C) const {
- if (!BT) {
- BT.reset(new APIMisuse(
- this, "message incorrectly sent to class instead of class instance"));
-
+ if (releaseS.isNull()) {
ASTContext &Ctx = C.getASTContext();
releaseS = GetNullarySelector("release", Ctx);
retainS = GetNullarySelector("retain", Ctx);
@@ -639,7 +631,7 @@ void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
"of class '" << Class->getName()
<< "' and not the class directly";
- auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
+ auto report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
report->addRange(msg.getSourceRange());
C.emitReport(std::move(report));
}
@@ -658,7 +650,8 @@ class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
mutable Selector orderedSetWithObjectsS;
mutable Selector initWithObjectsS;
mutable Selector initWithObjectsAndKeysS;
- mutable std::unique_ptr<BugType> BT;
+ const APIMisuse BT{this, "Arguments passed to variadic method aren't all "
+ "Objective-C pointer types"};
bool isVariadicMessage(const ObjCMethodCall &msg) const;
@@ -717,11 +710,7 @@ VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
CheckerContext &C) const {
- if (!BT) {
- BT.reset(new APIMisuse(this,
- "Arguments passed to variadic method aren't all "
- "Objective-C pointer types"));
-
+ if (arrayWithObjectsS.isNull()) {
ASTContext &Ctx = C.getASTContext();
arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
dictionaryWithObjectsAndKeysS =
@@ -792,8 +781,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
ArgTy.print(os, C.getLangOpts());
os << "'";
- auto R =
- std::make_unique<PathSensitiveBugReport>(*BT, os.str(), *errorNode);
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, os.str(), *errorNode);
R->addRange(msg.getArgSourceRange(I));
C.emitReport(std::move(R));
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 31cb150..fd0a398 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -78,35 +78,30 @@ static QualType getCharPtrType(ASTContext &Ctx, CharKind CK) {
: Ctx.WideCharTy);
}
-class CStringChecker : public Checker< eval::Call,
- check::PreStmt<DeclStmt>,
- check::LiveSymbols,
- check::DeadSymbols,
- check::RegionChanges
- > {
- mutable std::unique_ptr<BugType> BT_Null, BT_Bounds, BT_Overlap,
- BT_NotCString, BT_AdditionOverflow, BT_UninitRead;
-
+class CStringChecker
+ : public CheckerFamily<eval::Call, check::PreStmt<DeclStmt>,
+ check::LiveSymbols, check::DeadSymbols,
+ check::RegionChanges> {
mutable const char *CurrentFunctionDescription = nullptr;
public:
- /// The filter is used to filter out the diagnostics which are not enabled by
- /// the user.
- struct CStringChecksFilter {
- bool CheckCStringNullArg = false;
- bool CheckCStringOutOfBounds = false;
- bool CheckCStringBufferOverlap = false;
- bool CheckCStringNotNullTerm = false;
- bool CheckCStringUninitializedRead = false;
-
- CheckerNameRef CheckNameCStringNullArg;
- CheckerNameRef CheckNameCStringOutOfBounds;
- CheckerNameRef CheckNameCStringBufferOverlap;
- CheckerNameRef CheckNameCStringNotNullTerm;
- CheckerNameRef CheckNameCStringUninitializedRead;
- };
-
- CStringChecksFilter Filter;
+ // FIXME: The bug types emitted by this checker family have confused garbage
+ // in their Description and Category fields (e.g. `categories::UnixAPI` is
+ // passed as the description in several cases and `uninitialized` is mistyped
+ // as `unitialized`). This should be cleaned up.
+ CheckerFrontendWithBugType NullArg{categories::UnixAPI};
+ CheckerFrontendWithBugType OutOfBounds{"Out-of-bound array access"};
+ CheckerFrontendWithBugType BufferOverlap{categories::UnixAPI,
+ "Improper arguments"};
+ CheckerFrontendWithBugType NotNullTerm{categories::UnixAPI};
+ CheckerFrontendWithBugType UninitializedRead{
+ "Accessing unitialized/garbage values"};
+
+ // FIXME: This bug type should be removed because it is only emitted in a
+ // situation that is practically impossible.
+ const BugType AdditionOverflow{&OutOfBounds, "API"};
+
+ StringRef getDebugTag() const override { return "MallocChecker"; }
static void *getTag() { static int tag; return &tag; }
@@ -384,7 +379,7 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
assumeZero(C, State, l, Arg.Expression->getType());
if (stateNull && !stateNonNull) {
- if (Filter.CheckCStringNullArg) {
+ if (NullArg.isEnabled()) {
SmallString<80> buf;
llvm::raw_svector_ostream OS(buf);
assert(CurrentFunctionDescription);
@@ -468,7 +463,7 @@ ProgramStateRef CStringChecker::checkInit(CheckerContext &C,
return State;
// Ensure that we wouldn't read uninitialized value.
- if (Filter.CheckCStringUninitializedRead &&
+ if (UninitializedRead.isEnabled() &&
State->getSVal(*FirstElementVal).isUndef()) {
llvm::SmallString<258> Buf;
llvm::raw_svector_ostream OS(Buf);
@@ -524,7 +519,7 @@ ProgramStateRef CStringChecker::checkInit(CheckerContext &C,
if (!isa<Loc>(LastElementVal))
return State;
- if (Filter.CheckCStringUninitializedRead &&
+ if (UninitializedRead.isEnabled() &&
State->getSVal(LastElementVal.castAs<Loc>()).isUndef()) {
const llvm::APSInt *IdxInt = LastIdx.getAsInteger();
// If we can't get emit a sensible last element index, just bail out --
@@ -581,13 +576,9 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
auto [StInBound, StOutBound] = state->assumeInBoundDual(*Idx, Size);
if (StOutBound && !StInBound) {
- // These checks are either enabled by the CString out-of-bounds checker
- // explicitly or implicitly by the Malloc checker.
- // In the latter case we only do modeling but do not emit warning.
- if (!Filter.CheckCStringOutOfBounds)
+ if (!OutOfBounds.isEnabled())
return nullptr;
- // Emit a bug report.
ErrorMessage Message =
createOutOfBoundErrorMsg(CurrentFunctionDescription, Access);
emitOutOfBoundsBug(C, StOutBound, Buffer.Expression, Message);
@@ -620,7 +611,7 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
return nullptr;
// If out-of-bounds checking is turned off, skip the rest.
- if (!Filter.CheckCStringOutOfBounds)
+ if (!OutOfBounds.isEnabled())
return State;
SVal BufStart =
@@ -670,7 +661,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
SizeArgExpr Size, AnyArgExpr First,
AnyArgExpr Second,
CharKind CK) const {
- if (!Filter.CheckCStringBufferOverlap)
+ if (!BufferOverlap.isEnabled())
return state;
// Do a simple check for overlap: if the two arguments are from the same
@@ -789,13 +780,9 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state,
if (!N)
return;
- if (!BT_Overlap)
- BT_Overlap.reset(new BugType(Filter.CheckNameCStringBufferOverlap,
- categories::UnixAPI, "Improper arguments"));
-
// Generate a report for this bug.
auto report = std::make_unique<PathSensitiveBugReport>(
- *BT_Overlap, "Arguments must not be overlapping buffers", N);
+ BufferOverlap, "Arguments must not be overlapping buffers", N);
report->addRange(First->getSourceRange());
report->addRange(Second->getSourceRange());
@@ -805,15 +792,8 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state,
void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State,
const Stmt *S, StringRef WarningMsg) const {
if (ExplodedNode *N = C.generateErrorNode(State)) {
- if (!BT_Null) {
- // FIXME: This call uses the string constant 'categories::UnixAPI' as the
- // description of the bug; it should be replaced by a real description.
- BT_Null.reset(
- new BugType(Filter.CheckNameCStringNullArg, categories::UnixAPI));
- }
-
auto Report =
- std::make_unique<PathSensitiveBugReport>(*BT_Null, WarningMsg, N);
+ std::make_unique<PathSensitiveBugReport>(NullArg, WarningMsg, N);
Report->addRange(S->getSourceRange());
if (const auto *Ex = dyn_cast<Expr>(S))
bugreporter::trackExpressionValue(N, Ex, *Report);
@@ -826,12 +806,8 @@ void CStringChecker::emitUninitializedReadBug(CheckerContext &C,
const Expr *E, const MemRegion *R,
StringRef Msg) const {
if (ExplodedNode *N = C.generateErrorNode(State)) {
- if (!BT_UninitRead)
- BT_UninitRead.reset(new BugType(Filter.CheckNameCStringUninitializedRead,
- "Accessing unitialized/garbage values"));
-
auto Report =
- std::make_unique<PathSensitiveBugReport>(*BT_UninitRead, Msg, N);
+ std::make_unique<PathSensitiveBugReport>(UninitializedRead, Msg, N);
Report->addNote("Other elements might also be undefined",
Report->getLocation());
Report->addRange(E->getSourceRange());
@@ -845,17 +821,11 @@ void CStringChecker::emitOutOfBoundsBug(CheckerContext &C,
ProgramStateRef State, const Stmt *S,
StringRef WarningMsg) const {
if (ExplodedNode *N = C.generateErrorNode(State)) {
- if (!BT_Bounds)
- BT_Bounds.reset(new BugType(Filter.CheckCStringOutOfBounds
- ? Filter.CheckNameCStringOutOfBounds
- : Filter.CheckNameCStringNullArg,
- "Out-of-bound array access"));
-
// FIXME: It would be nice to eventually make this diagnostic more clear,
// e.g., by referencing the original declaration or by saying *why* this
// reference is outside the range.
auto Report =
- std::make_unique<PathSensitiveBugReport>(*BT_Bounds, WarningMsg, N);
+ std::make_unique<PathSensitiveBugReport>(OutOfBounds, WarningMsg, N);
Report->addRange(S->getSourceRange());
C.emitReport(std::move(Report));
}
@@ -865,15 +835,8 @@ void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
const Stmt *S,
StringRef WarningMsg) const {
if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
- if (!BT_NotCString) {
- // FIXME: This call uses the string constant 'categories::UnixAPI' as the
- // description of the bug; it should be replaced by a real description.
- BT_NotCString.reset(
- new BugType(Filter.CheckNameCStringNotNullTerm, categories::UnixAPI));
- }
-
auto Report =
- std::make_unique<PathSensitiveBugReport>(*BT_NotCString, WarningMsg, N);
+ std::make_unique<PathSensitiveBugReport>(NotNullTerm, WarningMsg, N);
Report->addRange(S->getSourceRange());
C.emitReport(std::move(Report));
@@ -883,14 +846,6 @@ void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
void CStringChecker::emitAdditionOverflowBug(CheckerContext &C,
ProgramStateRef State) const {
if (ExplodedNode *N = C.generateErrorNode(State)) {
- if (!BT_AdditionOverflow) {
- // FIXME: This call uses the word "API" as the description of the bug;
- // it should be replaced by a better error message (if this unlikely
- // situation continues to exist as a separate bug type).
- BT_AdditionOverflow.reset(
- new BugType(Filter.CheckNameCStringOutOfBounds, "API"));
- }
-
// This isn't a great error message, but this should never occur in real
// code anyway -- you'd have to create a buffer longer than a size_t can
// represent, which is sort of a contradiction.
@@ -898,7 +853,7 @@ void CStringChecker::emitAdditionOverflowBug(CheckerContext &C,
"This expression will create a string whose length is too big to "
"be represented as a size_t";
- auto Report = std::make_unique<PathSensitiveBugReport>(*BT_AdditionOverflow,
+ auto Report = std::make_unique<PathSensitiveBugReport>(AdditionOverflow,
WarningMsg, N);
C.emitReport(std::move(Report));
}
@@ -909,7 +864,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,
NonLoc left,
NonLoc right) const {
// If out-of-bounds checking is turned off, skip the rest.
- if (!Filter.CheckCStringOutOfBounds)
+ if (!OutOfBounds.isEnabled())
return state;
// If a previous check has failed, propagate the failure.
@@ -1048,7 +1003,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
// C string. In the context of locations, the only time we can issue such
// a warning is for labels.
if (std::optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) {
- if (Filter.CheckCStringNotNullTerm) {
+ if (NotNullTerm.isEnabled()) {
SmallString<120> buf;
llvm::raw_svector_ostream os(buf);
assert(CurrentFunctionDescription);
@@ -1110,7 +1065,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
// Other regions (mostly non-data) can't have a reliable C string length.
// In this case, an error is emitted and UndefinedVal is returned.
// The caller should always be prepared to handle this case.
- if (Filter.CheckCStringNotNullTerm) {
+ if (NotNullTerm.isEnabled()) {
SmallString<120> buf;
llvm::raw_svector_ostream os(buf);
@@ -2873,24 +2828,27 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR,
}
void ento::registerCStringModeling(CheckerManager &Mgr) {
- Mgr.registerChecker<CStringChecker>();
+ // Other checker relies on the modeling implemented in this checker family,
+ // so this "modeling checker" can register the 'CStringChecker' backend for
+ // its callbacks without enabling any of its frontends.
+ Mgr.getChecker<CStringChecker>();
}
-bool ento::shouldRegisterCStringModeling(const CheckerManager &mgr) {
+bool ento::shouldRegisterCStringModeling(const CheckerManager &) {
return true;
}
-#define REGISTER_CHECKER(name) \
- void ento::register##name(CheckerManager &mgr) { \
- CStringChecker *checker = mgr.getChecker<CStringChecker>(); \
- checker->Filter.Check##name = true; \
- checker->Filter.CheckName##name = mgr.getCurrentCheckerName(); \
+#define REGISTER_CHECKER(NAME) \
+ void ento::registerCString##NAME(CheckerManager &Mgr) { \
+ Mgr.getChecker<CStringChecker>()->NAME.enable(Mgr); \
} \
\
- bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
+ bool ento::shouldRegisterCString##NAME(const CheckerManager &) { \
+ return true; \
+ }
-REGISTER_CHECKER(CStringNullArg)
-REGISTER_CHECKER(CStringOutOfBounds)
-REGISTER_CHECKER(CStringBufferOverlap)
-REGISTER_CHECKER(CStringNotNullTerm)
-REGISTER_CHECKER(CStringUninitializedRead)
+REGISTER_CHECKER(NullArg)
+REGISTER_CHECKER(OutOfBounds)
+REGISTER_CHECKER(BufferOverlap)
+REGISTER_CHECKER(NotNullTerm)
+REGISTER_CHECKER(UninitializedRead)
diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
index 839c8bc..a227ca0 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
@@ -111,32 +111,12 @@ bool PlacementNewChecker::checkPlaceCapacityIsSufficient(
if (!SizeOfPlaceCI)
return true;
- if ((SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) ||
- (IsArrayTypeAllocated &&
- SizeOfPlaceCI->getValue() >= SizeOfTargetCI->getValue())) {
+ if ((SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue())) {
if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
- std::string Msg;
- // TODO: use clang constant
- if (IsArrayTypeAllocated &&
- SizeOfPlaceCI->getValue() > SizeOfTargetCI->getValue())
- Msg = std::string(llvm::formatv(
- "{0} bytes is possibly not enough for array allocation which "
- "requires {1} bytes. Current overhead requires the size of {2} "
- "bytes",
- SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue(),
- *SizeOfPlaceCI->getValue() - SizeOfTargetCI->getValue()));
- else if (IsArrayTypeAllocated &&
- SizeOfPlaceCI->getValue() == SizeOfTargetCI->getValue())
- Msg = std::string(llvm::formatv(
- "Storage provided to placement new is only {0} bytes, "
- "whereas the allocated array type requires more space for "
- "internal needs",
- SizeOfPlaceCI->getValue()));
- else
- Msg = std::string(llvm::formatv(
- "Storage provided to placement new is only {0} bytes, "
- "whereas the allocated type requires {1} bytes",
- SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
+ std::string Msg =
+ llvm::formatv("Storage provided to placement new is only {0} bytes, "
+ "whereas the allocated type requires {1} bytes",
+ SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue());
auto R = std::make_unique<PathSensitiveBugReport>(SBT, Msg, N);
bugreporter::trackExpressionValue(N, NE->getPlacementArg(0), *R);
diff --git a/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
index d7eea7e..152129e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
@@ -25,18 +25,22 @@ using namespace clang;
using namespace ento;
namespace {
+
+class DerefBugType : public BugType {
+ StringRef ArrayMsg, FieldMsg;
+
+public:
+ DerefBugType(CheckerFrontend *FE, StringRef Desc, const char *AMsg,
+ const char *FMsg = nullptr)
+ : BugType(FE, Desc), ArrayMsg(AMsg), FieldMsg(FMsg ? FMsg : AMsg) {}
+ StringRef getArrayMsg() const { return ArrayMsg; }
+ StringRef getFieldMsg() const { return FieldMsg; }
+};
+
class DereferenceChecker
- : public Checker< check::Location,
- check::Bind,
- EventDispatcher<ImplicitNullDerefEvent> > {
- enum DerefKind {
- NullPointer,
- UndefinedPointerValue,
- AddressOfLabel,
- FixedAddress,
- };
-
- void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S,
+ : public CheckerFamily<check::Location, check::Bind,
+ EventDispatcher<ImplicitNullDerefEvent>> {
+ void reportBug(const DerefBugType &BT, ProgramStateRef State, const Stmt *S,
CheckerContext &C) const;
bool suppressReport(CheckerContext &C, const Expr *E) const;
@@ -52,13 +56,23 @@ public:
const LocationContext *LCtx,
bool loadedFrom = false);
- bool CheckNullDereference = false;
- bool CheckFixedDereference = false;
-
- std::unique_ptr<BugType> BT_Null;
- std::unique_ptr<BugType> BT_Undef;
- std::unique_ptr<BugType> BT_Label;
- std::unique_ptr<BugType> BT_FixedAddress;
+ CheckerFrontend NullDerefChecker, FixedDerefChecker;
+ const DerefBugType NullBug{&NullDerefChecker, "Dereference of null pointer",
+ "a null pointer dereference",
+ "a dereference of a null pointer"};
+ const DerefBugType UndefBug{&NullDerefChecker,
+ "Dereference of undefined pointer value",
+ "an undefined pointer dereference",
+ "a dereference of an undefined pointer value"};
+ const DerefBugType LabelBug{&NullDerefChecker,
+ "Dereference of the address of a label",
+ "an undefined pointer dereference",
+ "a dereference of an address of a label"};
+ const DerefBugType FixedAddressBug{&FixedDerefChecker,
+ "Dereference of a fixed address",
+ "a dereference of a fixed address"};
+
+ StringRef getDebugTag() const override { return "DereferenceChecker"; }
};
} // end anonymous namespace
@@ -158,115 +172,87 @@ static bool isDeclRefExprToReference(const Expr *E) {
return false;
}
-void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State,
- const Stmt *S, CheckerContext &C) const {
- const BugType *BT = nullptr;
- llvm::StringRef DerefStr1;
- llvm::StringRef DerefStr2;
- switch (K) {
- case DerefKind::NullPointer:
- if (!CheckNullDereference) {
- C.addSink();
- return;
- }
- BT = BT_Null.get();
- DerefStr1 = " results in a null pointer dereference";
- DerefStr2 = " results in a dereference of a null pointer";
- break;
- case DerefKind::UndefinedPointerValue:
- if (!CheckNullDereference) {
- C.addSink();
+void DereferenceChecker::reportBug(const DerefBugType &BT,
+ ProgramStateRef State, const Stmt *S,
+ CheckerContext &C) const {
+ if (&BT == &FixedAddressBug) {
+ if (!FixedDerefChecker.isEnabled())
+ // Deliberately don't add a sink node if check is disabled.
+ // This situation may be valid in special cases.
return;
- }
- BT = BT_Undef.get();
- DerefStr1 = " results in an undefined pointer dereference";
- DerefStr2 = " results in a dereference of an undefined pointer value";
- break;
- case DerefKind::AddressOfLabel:
- if (!CheckNullDereference) {
+ } else {
+ if (!NullDerefChecker.isEnabled()) {
C.addSink();
return;
}
- BT = BT_Label.get();
- DerefStr1 = " results in an undefined pointer dereference";
- DerefStr2 = " results in a dereference of an address of a label";
- break;
- case DerefKind::FixedAddress:
- // Deliberately don't add a sink node if check is disabled.
- // This situation may be valid in special cases.
- if (!CheckFixedDereference)
- return;
-
- BT = BT_FixedAddress.get();
- DerefStr1 = " results in a dereference of a fixed address";
- DerefStr2 = " results in a dereference of a fixed address";
- break;
- };
+ }
// Generate an error node.
ExplodedNode *N = C.generateErrorNode(State);
if (!N)
return;
- SmallString<100> buf;
- llvm::raw_svector_ostream os(buf);
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Out(Buf);
SmallVector<SourceRange, 2> Ranges;
switch (S->getStmtClass()) {
case Stmt::ArraySubscriptExprClass: {
- os << "Array access";
+ Out << "Array access";
const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
- AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
- State.get(), N->getLocationContext());
- os << DerefStr1;
+ AddDerefSource(Out, Ranges, AE->getBase()->IgnoreParenCasts(), State.get(),
+ N->getLocationContext());
+ Out << " results in " << BT.getArrayMsg();
break;
}
case Stmt::ArraySectionExprClass: {
- os << "Array access";
+ Out << "Array access";
const ArraySectionExpr *AE = cast<ArraySectionExpr>(S);
- AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
- State.get(), N->getLocationContext());
- os << DerefStr1;
+ AddDerefSource(Out, Ranges, AE->getBase()->IgnoreParenCasts(), State.get(),
+ N->getLocationContext());
+ Out << " results in " << BT.getArrayMsg();
break;
}
case Stmt::UnaryOperatorClass: {
- os << BT->getDescription();
+ Out << BT.getDescription();
const UnaryOperator *U = cast<UnaryOperator>(S);
- AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
- State.get(), N->getLocationContext(), true);
+ AddDerefSource(Out, Ranges, U->getSubExpr()->IgnoreParens(), State.get(),
+ N->getLocationContext(), true);
break;
}
case Stmt::MemberExprClass: {
const MemberExpr *M = cast<MemberExpr>(S);
if (M->isArrow() || isDeclRefExprToReference(M->getBase())) {
- os << "Access to field '" << M->getMemberNameInfo() << "'" << DerefStr2;
- AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
- State.get(), N->getLocationContext(), true);
+ Out << "Access to field '" << M->getMemberNameInfo() << "' results in "
+ << BT.getFieldMsg();
+ AddDerefSource(Out, Ranges, M->getBase()->IgnoreParenCasts(), State.get(),
+ N->getLocationContext(), true);
}
break;
}
case Stmt::ObjCIvarRefExprClass: {
const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
- os << "Access to instance variable '" << *IV->getDecl() << "'" << DerefStr2;
- AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
- State.get(), N->getLocationContext(), true);
+ Out << "Access to instance variable '" << *IV->getDecl() << "' results in "
+ << BT.getFieldMsg();
+ AddDerefSource(Out, Ranges, IV->getBase()->IgnoreParenCasts(), State.get(),
+ N->getLocationContext(), true);
break;
}
default:
break;
}
- auto report = std::make_unique<PathSensitiveBugReport>(
- *BT, buf.empty() ? BT->getDescription() : buf.str(), N);
+ auto BR = std::make_unique<PathSensitiveBugReport>(
+ BT, Buf.empty() ? BT.getDescription() : Buf.str(), N);
- bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
+ bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *BR);
for (SmallVectorImpl<SourceRange>::iterator
I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
- report->addRange(*I);
+ BR->addRange(*I);
- C.emitReport(std::move(report));
+ C.emitReport(std::move(BR));
}
void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
@@ -275,7 +261,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
if (l.isUndef()) {
const Expr *DerefExpr = getDereferenceExpr(S);
if (!suppressReport(C, DerefExpr))
- reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C);
+ reportBug(UndefBug, C.getState(), DerefExpr, C);
return;
}
@@ -296,7 +282,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
// we call an "explicit" null dereference.
const Expr *expr = getDereferenceExpr(S);
if (!suppressReport(C, expr)) {
- reportBug(DerefKind::NullPointer, nullState, expr, C);
+ reportBug(NullBug, nullState, expr, C);
return;
}
}
@@ -314,7 +300,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
if (location.isConstant()) {
const Expr *DerefExpr = getDereferenceExpr(S, isLoad);
if (!suppressReport(C, DerefExpr))
- reportBug(DerefKind::FixedAddress, notNullState, DerefExpr, C);
+ reportBug(FixedAddressBug, notNullState, DerefExpr, C);
return;
}
@@ -330,7 +316,7 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
// One should never write to label addresses.
if (auto Label = L.getAs<loc::GotoLabel>()) {
- reportBug(DerefKind::AddressOfLabel, C.getState(), S, C);
+ reportBug(LabelBug, C.getState(), S, C);
return;
}
@@ -351,7 +337,7 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
if (!StNonNull) {
const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
if (!suppressReport(C, expr)) {
- reportBug(DerefKind::NullPointer, StNull, expr, C);
+ reportBug(NullBug, StNull, expr, C);
return;
}
}
@@ -369,7 +355,7 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
if (V.isConstant()) {
const Expr *DerefExpr = getDereferenceExpr(S, true);
if (!suppressReport(C, DerefExpr))
- reportBug(DerefKind::FixedAddress, State, DerefExpr, C);
+ reportBug(FixedAddressBug, State, DerefExpr, C);
return;
}
@@ -392,26 +378,8 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
C.addTransition(State, this);
}
-void ento::registerDereferenceModeling(CheckerManager &Mgr) {
- Mgr.registerChecker<DereferenceChecker>();
-}
-
-bool ento::shouldRegisterDereferenceModeling(const CheckerManager &) {
- return true;
-}
-
void ento::registerNullDereferenceChecker(CheckerManager &Mgr) {
- auto *Chk = Mgr.getChecker<DereferenceChecker>();
- Chk->CheckNullDereference = true;
- Chk->BT_Null.reset(new BugType(Mgr.getCurrentCheckerName(),
- "Dereference of null pointer",
- categories::LogicError));
- Chk->BT_Undef.reset(new BugType(Mgr.getCurrentCheckerName(),
- "Dereference of undefined pointer value",
- categories::LogicError));
- Chk->BT_Label.reset(new BugType(Mgr.getCurrentCheckerName(),
- "Dereference of the address of a label",
- categories::LogicError));
+ Mgr.getChecker<DereferenceChecker>()->NullDerefChecker.enable(Mgr);
}
bool ento::shouldRegisterNullDereferenceChecker(const CheckerManager &) {
@@ -419,11 +387,7 @@ bool ento::shouldRegisterNullDereferenceChecker(const CheckerManager &) {
}
void ento::registerFixedAddressDereferenceChecker(CheckerManager &Mgr) {
- auto *Chk = Mgr.getChecker<DereferenceChecker>();
- Chk->CheckFixedDereference = true;
- Chk->BT_FixedAddress.reset(new BugType(Mgr.getCurrentCheckerName(),
- "Dereference of a fixed address",
- categories::LogicError));
+ Mgr.getChecker<DereferenceChecker>()->FixedDerefChecker.enable(Mgr);
}
bool ento::shouldRegisterFixedAddressDereferenceChecker(
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 68efdba..369d619 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -2693,7 +2693,7 @@ void MallocChecker::HandleUseAfterFree(CheckerContext &C, SourceRange Range,
Frontend->UseFreeBug,
AF.Kind == AF_InnerBuffer
? "Inner pointer of container used after re/deallocation"
- : "Use of memory after it is freed",
+ : "Use of memory after it is released",
N);
R->markInteresting(Sym);
@@ -2721,8 +2721,8 @@ void MallocChecker::HandleDoubleFree(CheckerContext &C, SourceRange Range,
if (ExplodedNode *N = C.generateErrorNode()) {
auto R = std::make_unique<PathSensitiveBugReport>(
Frontend->DoubleFreeBug,
- (Released ? "Attempt to free released memory"
- : "Attempt to free non-owned memory"),
+ (Released ? "Attempt to release already released memory"
+ : "Attempt to release non-owned memory"),
N);
if (Range.isValid())
R->addRange(Range);
@@ -3730,13 +3730,15 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
return nullptr;
}
- // Save the first destructor/function as release point.
- assert(!ReleaseFunctionLC && "There should be only one release point");
+ // Record the stack frame that is _responsible_ for this memory release
+ // event. This will be used by the false positive suppression heuristics
+ // that recognize the release points of reference-counted objects.
+ //
+ // Usually (e.g. in C) we say that the _responsible_ stack frame is the
+ // current innermost stack frame:
ReleaseFunctionLC = CurrentLC->getStackFrame();
-
- // See if we're releasing memory while inlining a destructor that
- // decrement reference counters (or one of its callees).
- // This turns on various common false positive suppressions.
+ // ...but if the stack contains a destructor call, then we say that the
+ // outermost destructor stack frame is the _responsible_ one:
for (const LocationContext *LC = CurrentLC; LC; LC = LC->getParent()) {
if (const auto *DD = dyn_cast<CXXDestructorDecl>(LC->getDecl())) {
if (isReferenceCountingPointerDestructor(DD)) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
index 15fd9a0..d2760ca 100644
--- a/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
@@ -142,34 +142,19 @@ void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
//===----------------------------------------------------------------------===//
namespace {
+class NSOrCFErrorDerefChecker
+ : public CheckerFamily<check::Location,
+ check::Event<ImplicitNullDerefEvent>> {
+ mutable IdentifierInfo *NSErrorII = nullptr;
+ mutable IdentifierInfo *CFErrorII = nullptr;
-class NSErrorDerefBug : public BugType {
-public:
- NSErrorDerefBug(const CheckerNameRef Checker)
- : BugType(Checker, "NSError** null dereference",
- "Coding conventions (Apple)") {}
-};
-
-class CFErrorDerefBug : public BugType {
public:
- CFErrorDerefBug(const CheckerNameRef Checker)
- : BugType(Checker, "CFErrorRef* null dereference",
- "Coding conventions (Apple)") {}
-};
-
-}
+ CheckerFrontendWithBugType NSError{"NSError** null dereference",
+ "Coding conventions (Apple)"};
+ CheckerFrontendWithBugType CFError{"CFErrorRef* null dereference",
+ "Coding conventions (Apple)"};
-namespace {
-class NSOrCFErrorDerefChecker
- : public Checker< check::Location,
- check::Event<ImplicitNullDerefEvent> > {
- mutable IdentifierInfo *NSErrorII, *CFErrorII;
- mutable std::unique_ptr<NSErrorDerefBug> NSBT;
- mutable std::unique_ptr<CFErrorDerefBug> CFBT;
-public:
- bool ShouldCheckNSError = false, ShouldCheckCFError = false;
- CheckerNameRef NSErrorName, CFErrorName;
- NSOrCFErrorDerefChecker() : NSErrorII(nullptr), CFErrorII(nullptr) {}
+ StringRef getDebugTag() const override { return "NSOrCFErrorDerefChecker"; }
void checkLocation(SVal loc, bool isLoad, const Stmt *S,
CheckerContext &C) const;
@@ -236,12 +221,12 @@ void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad,
if (!CFErrorII)
CFErrorII = &Ctx.Idents.get("CFErrorRef");
- if (ShouldCheckNSError && IsNSError(parmT, NSErrorII)) {
+ if (NSError.isEnabled() && IsNSError(parmT, NSErrorII)) {
setFlag<NSErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C);
return;
}
- if (ShouldCheckCFError && IsCFError(parmT, CFErrorII)) {
+ if (CFError.isEnabled() && IsCFError(parmT, CFErrorII)) {
setFlag<CFErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C);
return;
}
@@ -274,19 +259,9 @@ void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const {
os << " may be null";
- BugType *bug = nullptr;
- if (isNSError) {
- if (!NSBT)
- NSBT.reset(new NSErrorDerefBug(NSErrorName));
- bug = NSBT.get();
- }
- else {
- if (!CFBT)
- CFBT.reset(new CFErrorDerefBug(CFErrorName));
- bug = CFBT.get();
- }
+ const BugType &BT = isNSError ? NSError : CFError;
BR.emitReport(
- std::make_unique<PathSensitiveBugReport>(*bug, os.str(), event.SinkNode));
+ std::make_unique<PathSensitiveBugReport>(BT, os.str(), event.SinkNode));
}
static bool IsNSError(QualType T, IdentifierInfo *II) {
@@ -320,32 +295,21 @@ static bool IsCFError(QualType T, IdentifierInfo *II) {
return TT->getDecl()->getIdentifier() == II;
}
-void ento::registerNSOrCFErrorDerefChecker(CheckerManager &mgr) {
- mgr.registerChecker<NSOrCFErrorDerefChecker>();
-}
-
-bool ento::shouldRegisterNSOrCFErrorDerefChecker(const CheckerManager &mgr) {
- return true;
-}
-
-void ento::registerNSErrorChecker(CheckerManager &mgr) {
- mgr.registerChecker<NSErrorMethodChecker>();
- NSOrCFErrorDerefChecker *checker = mgr.getChecker<NSOrCFErrorDerefChecker>();
- checker->ShouldCheckNSError = true;
- checker->NSErrorName = mgr.getCurrentCheckerName();
-}
-
-bool ento::shouldRegisterNSErrorChecker(const CheckerManager &mgr) {
- return true;
-}
-
-void ento::registerCFErrorChecker(CheckerManager &mgr) {
- mgr.registerChecker<CFErrorFunctionChecker>();
- NSOrCFErrorDerefChecker *checker = mgr.getChecker<NSOrCFErrorDerefChecker>();
- checker->ShouldCheckCFError = true;
- checker->CFErrorName = mgr.getCurrentCheckerName();
-}
+// This source file implements two user-facing checkers ("osx.cocoa.NSError"
+// and "osx.coreFoundation.CFError") which are both implemented as the
+// combination of two `CheckerFrontend`s that are registered under the same
+// name (but otherwise act independently). Among these 2+2 `CheckerFrontend`s
+// two are coming from the checker family `NSOrCFErrorDerefChecker` while the
+// other two (the `ADDITIONAL_PART`s) are small standalone checkers.
+#define REGISTER_CHECKER(NAME, ADDITIONAL_PART) \
+ void ento::register##NAME##Checker(CheckerManager &Mgr) { \
+ Mgr.getChecker<NSOrCFErrorDerefChecker>()->NAME.enable(Mgr); \
+ Mgr.registerChecker<ADDITIONAL_PART>(); \
+ } \
+ \
+ bool ento::shouldRegister##NAME##Checker(const CheckerManager &) { \
+ return true; \
+ }
-bool ento::shouldRegisterCFErrorChecker(const CheckerManager &mgr) {
- return true;
-}
+REGISTER_CHECKER(NSError, NSErrorMethodChecker)
+REGISTER_CHECKER(CFError, CFErrorFunctionChecker)
diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index a63497c..019e81f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -28,23 +28,22 @@ using namespace ento;
namespace {
class StackAddrEscapeChecker
- : public Checker<check::PreCall, check::PreStmt<ReturnStmt>,
- check::EndFunction> {
+ : public CheckerFamily<check::PreCall, check::PreStmt<ReturnStmt>,
+ check::EndFunction> {
mutable IdentifierInfo *dispatch_semaphore_tII = nullptr;
- mutable std::unique_ptr<BugType> BT_stackleak;
- mutable std::unique_ptr<BugType> BT_returnstack;
- mutable std::unique_ptr<BugType> BT_capturedstackasync;
- mutable std::unique_ptr<BugType> BT_capturedstackret;
public:
- enum CheckKind {
- CK_StackAddrEscapeChecker,
- CK_StackAddrAsyncEscapeChecker,
- CK_NumCheckKinds
- };
+ StringRef getDebugTag() const override { return "StackAddrEscapeChecker"; }
+
+ CheckerFrontend StackAddrEscape;
+ CheckerFrontend StackAddrAsyncEscape;
- bool ChecksEnabled[CK_NumCheckKinds] = {false};
- CheckerNameRef CheckNames[CK_NumCheckKinds];
+ const BugType StackLeak{&StackAddrEscape,
+ "Stack address leaks outside of stack frame"};
+ const BugType ReturnStack{&StackAddrEscape,
+ "Return of address to stack-allocated memory"};
+ const BugType CapturedStackAsync{
+ &StackAddrAsyncEscape, "Address of stack-allocated memory is captured"};
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
@@ -170,10 +169,6 @@ void StackAddrEscapeChecker::EmitReturnLeakError(CheckerContext &C,
ExplodedNode *N = C.generateNonFatalErrorNode();
if (!N)
return;
- if (!BT_returnstack)
- BT_returnstack = std::make_unique<BugType>(
- CheckNames[CK_StackAddrEscapeChecker],
- "Return of address to stack-allocated memory");
// Generate a report for this bug.
SmallString<128> buf;
@@ -184,7 +179,7 @@ void StackAddrEscapeChecker::EmitReturnLeakError(CheckerContext &C,
EmitReturnedAsPartOfError(os, C.getSVal(RetE), R);
auto report =
- std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N);
+ std::make_unique<PathSensitiveBugReport>(ReturnStack, os.str(), N);
report->addRange(RetE->getSourceRange());
if (range.isValid())
report->addRange(range);
@@ -215,16 +210,12 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
ExplodedNode *N = C.generateNonFatalErrorNode();
if (!N)
continue;
- if (!BT_capturedstackasync)
- BT_capturedstackasync = std::make_unique<BugType>(
- CheckNames[CK_StackAddrAsyncEscapeChecker],
- "Address of stack-allocated memory is captured");
SmallString<128> Buf;
llvm::raw_svector_ostream Out(Buf);
SourceRange Range = genName(Out, Region, C.getASTContext());
Out << " is captured by an asynchronously-executed block";
- auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT_capturedstackasync, Out.str(), N);
+ auto Report = std::make_unique<PathSensitiveBugReport>(CapturedStackAsync,
+ Out.str(), N);
if (Range.isValid())
Report->addRange(Range);
C.emitReport(std::move(Report));
@@ -233,7 +224,7 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
- if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
+ if (!StackAddrAsyncEscape.isEnabled())
return;
if (!Call.isGlobalCFunction("dispatch_after") &&
!Call.isGlobalCFunction("dispatch_async"))
@@ -357,7 +348,7 @@ FindEscapingStackRegions(CheckerContext &C, const Expr *RetE, SVal RetVal) {
void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
CheckerContext &C) const {
- if (!ChecksEnabled[CK_StackAddrEscapeChecker])
+ if (!StackAddrEscape.isEnabled())
return;
const Expr *RetE = RS->getRetValue();
@@ -456,7 +447,7 @@ static bool isInvalidatedSymbolRegion(const MemRegion *Region) {
void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
CheckerContext &Ctx) const {
- if (!ChecksEnabled[CK_StackAddrEscapeChecker])
+ if (!StackAddrEscape.isEnabled())
return;
ExplodedNode *Node = Ctx.getPredecessor();
@@ -581,11 +572,6 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
if (!N)
return;
- if (!BT_stackleak)
- BT_stackleak =
- std::make_unique<BugType>(CheckNames[CK_StackAddrEscapeChecker],
- "Stack address leaks outside of stack frame");
-
for (const auto &P : Cb.V) {
const MemRegion *Referrer = P.first->getBaseRegion();
const MemRegion *Referred = P.second;
@@ -604,7 +590,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
Out << " is still referred to by a temporary object on the stack"
<< CommonSuffix;
auto Report =
- std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
+ std::make_unique<PathSensitiveBugReport>(StackLeak, Out.str(), N);
if (Range.isValid())
Report->addRange(Range);
Ctx.emitReport(std::move(Report));
@@ -618,7 +604,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
Out << " is still referred to by the " << *ReferrerVariable << CommonSuffix;
auto Report =
- std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
+ std::make_unique<PathSensitiveBugReport>(StackLeak, Out.str(), N);
if (Range.isValid())
Report->addRange(Range);
@@ -626,23 +612,14 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
}
}
-void ento::registerStackAddrEscapeBase(CheckerManager &mgr) {
- mgr.registerChecker<StackAddrEscapeChecker>();
-}
-
-bool ento::shouldRegisterStackAddrEscapeBase(const CheckerManager &mgr) {
- return true;
-}
-
-#define REGISTER_CHECKER(name) \
- void ento::register##name(CheckerManager &Mgr) { \
- StackAddrEscapeChecker *Chk = Mgr.getChecker<StackAddrEscapeChecker>(); \
- Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \
- Chk->CheckNames[StackAddrEscapeChecker::CK_##name] = \
- Mgr.getCurrentCheckerName(); \
+#define REGISTER_CHECKER(NAME) \
+ void ento::register##NAME##Checker(CheckerManager &Mgr) { \
+ Mgr.getChecker<StackAddrEscapeChecker>()->NAME.enable(Mgr); \
} \
\
- bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
+ bool ento::shouldRegister##NAME##Checker(const CheckerManager &) { \
+ return true; \
+ }
-REGISTER_CHECKER(StackAddrEscapeChecker)
-REGISTER_CHECKER(StackAddrAsyncEscapeChecker)
+REGISTER_CHECKER(StackAddrEscape)
+REGISTER_CHECKER(StackAddrAsyncEscape)