mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-04 00:06:50 +00:00
Expose the name of the checker producing each diagnostic message.
Summary: In clang-tidy we'd like to know the name of the checker producing each diagnostic message. PathDiagnostic has BugType and Category fields, which are both arbitrary human-readable strings, but we need to know the exact name of the checker in the form that can be used in the CheckersControlList option to enable/disable the specific checker. This patch adds the CheckName field to the CheckerBase class, and sets it in the CheckerManager::registerChecker() method, which gets them from the CheckerRegistry. Checkers that implement multiple checks have to store the names of each check in the respective registerXXXChecker method. Reviewers: jordan_rose, krememek Reviewed By: jordan_rose CC: cfe-commits Differential Revision: http://llvm-reviews.chandlerc.com/D2557 llvm-svn: 201186
This commit is contained in:
parent
6bd395f3f0
commit
4aca9b1cd8
@ -35,7 +35,7 @@ void MainCallChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const
|
||||
return;
|
||||
|
||||
if (!BT)
|
||||
BT.reset(new BugType("call to main", "example analyzer plugin"));
|
||||
BT.reset(new BugType(this, "call to main", "example analyzer plugin"));
|
||||
|
||||
BugReport *report = new BugReport(*BT, BT->getName(), N);
|
||||
report->addRange(Callee->getSourceRange());
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
|
||||
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h"
|
||||
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
|
||||
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/FoldingSet.h"
|
||||
@ -463,7 +464,12 @@ public:
|
||||
/// reports.
|
||||
void emitReport(BugReport *R);
|
||||
|
||||
void EmitBasicReport(const Decl *DeclWithIssue,
|
||||
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker,
|
||||
StringRef BugName, StringRef BugCategory,
|
||||
StringRef BugStr, PathDiagnosticLocation Loc,
|
||||
ArrayRef<SourceRange> Ranges = None);
|
||||
|
||||
void EmitBasicReport(const Decl *DeclWithIssue, CheckName CheckName,
|
||||
StringRef BugName, StringRef BugCategory,
|
||||
StringRef BugStr, PathDiagnosticLocation Loc,
|
||||
ArrayRef<SourceRange> Ranges = None);
|
||||
@ -473,7 +479,8 @@ private:
|
||||
|
||||
/// \brief Returns a BugType that is associated with the given name and
|
||||
/// category.
|
||||
BugType *getBugTypeForName(StringRef name, StringRef category);
|
||||
BugType *getBugTypeForName(CheckName CheckName, StringRef name,
|
||||
StringRef category);
|
||||
};
|
||||
|
||||
// FIXME: Get rid of GRBugReporter. It's the wrong abstraction.
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
|
||||
#include "clang/StaticAnalyzer/Core/Checker.h"
|
||||
#include "llvm/ADT/FoldingSet.h"
|
||||
#include <string>
|
||||
|
||||
@ -29,20 +30,25 @@ class ExprEngine;
|
||||
|
||||
class BugType {
|
||||
private:
|
||||
const CheckName CheckName;
|
||||
const std::string Name;
|
||||
const std::string Category;
|
||||
bool SuppressonSink;
|
||||
|
||||
virtual void anchor();
|
||||
public:
|
||||
BugType(StringRef name, StringRef cat)
|
||||
: Name(name), Category(cat), SuppressonSink(false) {}
|
||||
BugType(class CheckName Check, StringRef name, StringRef cat)
|
||||
: CheckName(Check), Name(name), Category(cat), SuppressonSink(false) {}
|
||||
BugType(const CheckerBase *checker, StringRef name, StringRef cat)
|
||||
: CheckName(checker->getCheckName()), Name(name), Category(cat),
|
||||
SuppressonSink(false) {}
|
||||
virtual ~BugType() {}
|
||||
|
||||
// FIXME: Should these be made strings as well?
|
||||
StringRef getName() const { return Name; }
|
||||
StringRef getCategory() const { return Category; }
|
||||
|
||||
StringRef getCheckName() const { return CheckName.getName(); }
|
||||
|
||||
/// isSuppressOnSink - Returns true if bug reports associated with this bug
|
||||
/// type should be suppressed if the end node of the report is post-dominated
|
||||
/// by a sink node.
|
||||
@ -56,12 +62,17 @@ class BuiltinBug : public BugType {
|
||||
const std::string desc;
|
||||
virtual void anchor();
|
||||
public:
|
||||
BuiltinBug(const char *name, const char *description)
|
||||
: BugType(name, categories::LogicError), desc(description) {}
|
||||
|
||||
BuiltinBug(const char *name)
|
||||
: BugType(name, categories::LogicError), desc(name) {}
|
||||
|
||||
BuiltinBug(class CheckName CheckName, const char *name,
|
||||
const char *description)
|
||||
: BugType(CheckName, name, categories::LogicError), desc(description) {}
|
||||
|
||||
BuiltinBug(const CheckerBase *checker, const char *name,
|
||||
const char *description)
|
||||
: BugType(checker, name, categories::LogicError), desc(description) {}
|
||||
|
||||
BuiltinBug(const CheckerBase *checker, const char *name)
|
||||
: BugType(checker, name, categories::LogicError), desc(name) {}
|
||||
|
||||
StringRef getDescription() const { return desc; }
|
||||
};
|
||||
|
||||
|
@ -710,6 +710,7 @@ public:
|
||||
/// diagnostic. It represents an ordered-collection of PathDiagnosticPieces,
|
||||
/// each which represent the pieces of the path.
|
||||
class PathDiagnostic : public llvm::FoldingSetNode {
|
||||
std::string CheckName;
|
||||
const Decl *DeclWithIssue;
|
||||
std::string BugType;
|
||||
std::string VerboseDesc;
|
||||
@ -730,8 +731,8 @@ class PathDiagnostic : public llvm::FoldingSetNode {
|
||||
|
||||
PathDiagnostic() LLVM_DELETED_FUNCTION;
|
||||
public:
|
||||
PathDiagnostic(const Decl *DeclWithIssue, StringRef bugtype,
|
||||
StringRef verboseDesc, StringRef shortDesc,
|
||||
PathDiagnostic(StringRef CheckName, const Decl *DeclWithIssue,
|
||||
StringRef bugtype, StringRef verboseDesc, StringRef shortDesc,
|
||||
StringRef category, PathDiagnosticLocation LocationToUnique,
|
||||
const Decl *DeclToUnique);
|
||||
|
||||
@ -788,6 +789,7 @@ public:
|
||||
StringRef getShortDescription() const {
|
||||
return ShortDesc.empty() ? VerboseDesc : ShortDesc;
|
||||
}
|
||||
StringRef getCheckName() const { return CheckName; }
|
||||
StringRef getBugType() const { return BugType; }
|
||||
StringRef getCategory() const { return Category; }
|
||||
|
||||
|
@ -453,14 +453,18 @@ public:
|
||||
} // end eval namespace
|
||||
|
||||
class CheckerBase : public ProgramPointTag {
|
||||
CheckName Name;
|
||||
friend class ::clang::ento::CheckerManager;
|
||||
|
||||
public:
|
||||
StringRef getTagDescription() const;
|
||||
CheckName getCheckName() const;
|
||||
|
||||
/// See CheckerManager::runCheckersForPrintState.
|
||||
virtual void printState(raw_ostream &Out, ProgramStateRef State,
|
||||
const char *NL, const char *Sep) const { }
|
||||
};
|
||||
|
||||
|
||||
template <typename CHECK1, typename CHECK2=check::_VoidCheck,
|
||||
typename CHECK3=check::_VoidCheck, typename CHECK4=check::_VoidCheck,
|
||||
typename CHECK5=check::_VoidCheck, typename CHECK6=check::_VoidCheck,
|
||||
|
@ -29,6 +29,7 @@ namespace clang {
|
||||
|
||||
namespace ento {
|
||||
class CheckerBase;
|
||||
class CheckerRegistry;
|
||||
class ExprEngine;
|
||||
class AnalysisManager;
|
||||
class BugReporter;
|
||||
@ -131,9 +132,26 @@ enum PointerEscapeKind {
|
||||
PSK_EscapeOther
|
||||
};
|
||||
|
||||
// This wrapper is used to ensure that only StringRefs originating from the
|
||||
// CheckerRegistry are used as check names. We want to make sure all check
|
||||
// name strings have a lifetime that keeps them alive at least until the path
|
||||
// diagnostics have been processed.
|
||||
class CheckName {
|
||||
StringRef Name;
|
||||
friend class ::clang::ento::CheckerRegistry;
|
||||
explicit CheckName(StringRef Name) : Name(Name) {}
|
||||
|
||||
public:
|
||||
CheckName() {}
|
||||
CheckName(const CheckName &Other) : Name(Other.Name) {}
|
||||
StringRef getName() const { return Name; }
|
||||
};
|
||||
|
||||
class CheckerManager {
|
||||
const LangOptions LangOpts;
|
||||
AnalyzerOptionsRef AOptions;
|
||||
CheckName CurrentCheckName;
|
||||
|
||||
public:
|
||||
CheckerManager(const LangOptions &langOpts,
|
||||
AnalyzerOptionsRef AOptions)
|
||||
@ -142,6 +160,9 @@ public:
|
||||
|
||||
~CheckerManager();
|
||||
|
||||
void setCurrentCheckName(CheckName name) { CurrentCheckName = name; }
|
||||
CheckName getCurrentCheckName() const { return CurrentCheckName; }
|
||||
|
||||
bool hasPathSensitiveCheckers() const;
|
||||
|
||||
void finishedCheckerRegistration();
|
||||
@ -168,6 +189,7 @@ public:
|
||||
return static_cast<CHECKER *>(ref); // already registered.
|
||||
|
||||
CHECKER *checker = new CHECKER();
|
||||
checker->Name = CurrentCheckName;
|
||||
CheckerDtors.push_back(CheckerDtor(checker, destruct<CHECKER>));
|
||||
CHECKER::_register(checker, *this);
|
||||
ref = checker;
|
||||
@ -182,6 +204,7 @@ public:
|
||||
return static_cast<CHECKER *>(ref); // already registered.
|
||||
|
||||
CHECKER *checker = new CHECKER(AOpts);
|
||||
checker->Name = CurrentCheckName;
|
||||
CheckerDtors.push_back(CheckerDtor(checker, destruct<CHECKER>));
|
||||
CHECKER::_register(checker, *this);
|
||||
ref = checker;
|
||||
|
@ -112,7 +112,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
|
||||
<< " | Empty WorkList: "
|
||||
<< (Eng.hasEmptyWorkList() ? "yes" : "no");
|
||||
|
||||
B.EmitBasicReport(D, "Analyzer Statistics", "Internal Statistics",
|
||||
B.EmitBasicReport(D, this, "Analyzer Statistics", "Internal Statistics",
|
||||
output.str(), PathDiagnosticLocation(D, SM));
|
||||
|
||||
// Emit warning for each block we bailed out on.
|
||||
@ -129,7 +129,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
|
||||
outputI << "(" << NameOfRootFunction << ")" <<
|
||||
": The analyzer generated a sink at this point";
|
||||
B.EmitBasicReport(
|
||||
D, "Sink Point", "Internal Statistics", outputI.str(),
|
||||
D, this, "Sink Point", "Internal Statistics", outputI.str(),
|
||||
PathDiagnosticLocation::createBegin(CS->getStmt(), SM, LC));
|
||||
}
|
||||
}
|
||||
|
@ -66,8 +66,9 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS,
|
||||
return;
|
||||
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug("Out-of-bound array access",
|
||||
"Access out-of-bound array element (buffer overflow)"));
|
||||
BT.reset(new BuiltinBug(
|
||||
this, "Out-of-bound array access",
|
||||
"Access out-of-bound array element (buffer overflow)"));
|
||||
|
||||
// FIXME: It would be nice to eventually make this diagnostic more clear,
|
||||
// e.g., by referencing the original declaration or by saying *why* this
|
||||
|
@ -187,7 +187,7 @@ void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext,
|
||||
return;
|
||||
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug("Out-of-bound access"));
|
||||
BT.reset(new BuiltinBug(this, "Out-of-bound access"));
|
||||
|
||||
// FIXME: This diagnostics are preliminary. We should get far better
|
||||
// diagnostics for explaining buffer overruns.
|
||||
@ -311,7 +311,6 @@ RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state,
|
||||
return RegionRawOffsetV2();
|
||||
}
|
||||
|
||||
|
||||
void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) {
|
||||
mgr.registerChecker<ArrayBoundCheckerV2>();
|
||||
}
|
||||
|
@ -39,7 +39,8 @@ using namespace ento;
|
||||
namespace {
|
||||
class APIMisuse : public BugType {
|
||||
public:
|
||||
APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
|
||||
APIMisuse(const CheckerBase *checker, const char *name)
|
||||
: BugType(checker, name, "API Misuse (Apple)") {}
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
@ -191,7 +192,7 @@ void NilArgChecker::generateBugReport(ExplodedNode *N,
|
||||
const Expr *E,
|
||||
CheckerContext &C) const {
|
||||
if (!BT)
|
||||
BT.reset(new APIMisuse("nil argument"));
|
||||
BT.reset(new APIMisuse(this, "nil argument"));
|
||||
|
||||
BugReport *R = new BugReport(*BT, Msg, N);
|
||||
R->addRange(Range);
|
||||
@ -483,8 +484,8 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
|
||||
<< " bits of the input integer will be lost.";
|
||||
|
||||
if (!BT)
|
||||
BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
|
||||
|
||||
BT.reset(new APIMisuse(this, "Bad use of CFNumberCreate"));
|
||||
|
||||
BugReport *report = new BugReport(*BT, os.str(), N);
|
||||
report->addRange(CE->getArg(2)->getSourceRange());
|
||||
C.emitReport(report);
|
||||
@ -522,8 +523,8 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
|
||||
Retain = &Ctx.Idents.get("CFRetain");
|
||||
Release = &Ctx.Idents.get("CFRelease");
|
||||
MakeCollectable = &Ctx.Idents.get("CFMakeCollectable");
|
||||
BT.reset(
|
||||
new APIMisuse("null passed to CFRetain/CFRelease/CFMakeCollectable"));
|
||||
BT.reset(new APIMisuse(
|
||||
this, "null passed to CFRetain/CFRelease/CFMakeCollectable"));
|
||||
}
|
||||
|
||||
// Check if we called CFRetain/CFRelease/CFMakeCollectable.
|
||||
@ -600,9 +601,9 @@ void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
||||
CheckerContext &C) const {
|
||||
|
||||
if (!BT) {
|
||||
BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
|
||||
"instance"));
|
||||
|
||||
BT.reset(new APIMisuse(
|
||||
this, "message incorrectly sent to class instead of class instance"));
|
||||
|
||||
ASTContext &Ctx = C.getASTContext();
|
||||
releaseS = GetNullarySelector("release", Ctx);
|
||||
retainS = GetNullarySelector("retain", Ctx);
|
||||
@ -708,7 +709,8 @@ VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
|
||||
void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
||||
CheckerContext &C) const {
|
||||
if (!BT) {
|
||||
BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
|
||||
BT.reset(new APIMisuse(this,
|
||||
"Arguments passed to variadic method aren't all "
|
||||
"Objective-C pointer types"));
|
||||
|
||||
ASTContext &Ctx = C.getASTContext();
|
||||
@ -1263,6 +1265,7 @@ void ento::registerObjCLoopChecker(CheckerManager &mgr) {
|
||||
mgr.registerChecker<ObjCLoopChecker>();
|
||||
}
|
||||
|
||||
void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
|
||||
void
|
||||
ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
|
||||
mgr.registerChecker<ObjCNonNilReturnValueChecker>();
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ void BoolAssignmentChecker::emitReport(ProgramStateRef state,
|
||||
CheckerContext &C) const {
|
||||
if (ExplodedNode *N = C.addTransition(state)) {
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug("Assignment of a non-Boolean value"));
|
||||
BT.reset(new BuiltinBug(this, "Assignment of a non-Boolean value"));
|
||||
C.emitReport(new BugReport(*BT, BT->getDescription(), N));
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,11 @@ public:
|
||||
DefaultBool CheckCStringOutOfBounds;
|
||||
DefaultBool CheckCStringBufferOverlap;
|
||||
DefaultBool CheckCStringNotNullTerm;
|
||||
|
||||
CheckName CheckNameCStringNullArg;
|
||||
CheckName CheckNameCStringOutOfBounds;
|
||||
CheckName CheckNameCStringBufferOverlap;
|
||||
CheckName CheckNameCStringNotNullTerm;
|
||||
};
|
||||
|
||||
CStringChecksFilter Filter;
|
||||
@ -232,8 +237,9 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
|
||||
return NULL;
|
||||
|
||||
if (!BT_Null)
|
||||
BT_Null.reset(new BuiltinBug(categories::UnixAPI,
|
||||
"Null pointer argument in call to byte string function"));
|
||||
BT_Null.reset(new BuiltinBug(
|
||||
Filter.CheckNameCStringNullArg, categories::UnixAPI,
|
||||
"Null pointer argument in call to byte string function"));
|
||||
|
||||
SmallString<80> buf;
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
@ -294,8 +300,9 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
|
||||
return NULL;
|
||||
|
||||
if (!BT_Bounds) {
|
||||
BT_Bounds.reset(new BuiltinBug("Out-of-bound array access",
|
||||
"Byte string function accesses out-of-bound array element"));
|
||||
BT_Bounds.reset(new BuiltinBug(
|
||||
Filter.CheckNameCStringOutOfBounds, "Out-of-bound array access",
|
||||
"Byte string function accesses out-of-bound array element"));
|
||||
}
|
||||
BuiltinBug *BT = static_cast<BuiltinBug*>(BT_Bounds.get());
|
||||
|
||||
@ -526,7 +533,8 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state,
|
||||
return;
|
||||
|
||||
if (!BT_Overlap)
|
||||
BT_Overlap.reset(new BugType(categories::UnixAPI, "Improper arguments"));
|
||||
BT_Overlap.reset(new BugType(Filter.CheckNameCStringBufferOverlap,
|
||||
categories::UnixAPI, "Improper arguments"));
|
||||
|
||||
// Generate a report for this bug.
|
||||
BugReport *report =
|
||||
@ -586,8 +594,9 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,
|
||||
return NULL;
|
||||
|
||||
if (!BT_AdditionOverflow)
|
||||
BT_AdditionOverflow.reset(new BuiltinBug("API",
|
||||
"Sum of expressions causes overflow"));
|
||||
BT_AdditionOverflow.reset(
|
||||
new BuiltinBug(Filter.CheckNameCStringOutOfBounds, "API",
|
||||
"Sum of expressions causes overflow"));
|
||||
|
||||
// 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
|
||||
@ -703,8 +712,9 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
|
||||
|
||||
if (ExplodedNode *N = C.addTransition(state)) {
|
||||
if (!BT_NotCString)
|
||||
BT_NotCString.reset(new BuiltinBug(categories::UnixAPI,
|
||||
"Argument is not a null-terminated string."));
|
||||
BT_NotCString.reset(new BuiltinBug(
|
||||
Filter.CheckNameCStringNotNullTerm, categories::UnixAPI,
|
||||
"Argument is not a null-terminated string."));
|
||||
|
||||
SmallString<120> buf;
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
@ -714,8 +724,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
|
||||
<< "', which is not a null-terminated string";
|
||||
|
||||
// Generate a report for this bug.
|
||||
BugReport *report = new BugReport(*BT_NotCString,
|
||||
os.str(), N);
|
||||
BugReport *report = new BugReport(*BT_NotCString, os.str(), N);
|
||||
|
||||
report->addRange(Ex->getSourceRange());
|
||||
C.emitReport(report);
|
||||
@ -763,8 +772,9 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
|
||||
|
||||
if (ExplodedNode *N = C.addTransition(state)) {
|
||||
if (!BT_NotCString)
|
||||
BT_NotCString.reset(new BuiltinBug(categories::UnixAPI,
|
||||
"Argument is not a null-terminated string."));
|
||||
BT_NotCString.reset(new BuiltinBug(
|
||||
Filter.CheckNameCStringNotNullTerm, categories::UnixAPI,
|
||||
"Argument is not a null-terminated string."));
|
||||
|
||||
SmallString<120> buf;
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
@ -2057,10 +2067,12 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR,
|
||||
C.addTransition(state);
|
||||
}
|
||||
|
||||
#define REGISTER_CHECKER(name) \
|
||||
void ento::register##name(CheckerManager &mgr) {\
|
||||
mgr.registerChecker<CStringChecker>()->Filter.Check##name = true; \
|
||||
}
|
||||
#define REGISTER_CHECKER(name) \
|
||||
void ento::register##name(CheckerManager &mgr) { \
|
||||
CStringChecker *checker = mgr.registerChecker<CStringChecker>(); \
|
||||
checker->Filter.Check##name = true; \
|
||||
checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \
|
||||
}
|
||||
|
||||
REGISTER_CHECKER(CStringNullArg)
|
||||
REGISTER_CHECKER(CStringOutOfBounds)
|
||||
|
@ -31,6 +31,7 @@ using namespace ento;
|
||||
|
||||
namespace {
|
||||
class WalkAST: public StmtVisitor<WalkAST> {
|
||||
const CheckerBase *Checker;
|
||||
BugReporter &BR;
|
||||
AnalysisDeclContext* AC;
|
||||
|
||||
@ -81,9 +82,8 @@ class WalkAST: public StmtVisitor<WalkAST> {
|
||||
bool containsBadStrncatPattern(const CallExpr *CE);
|
||||
|
||||
public:
|
||||
WalkAST(BugReporter &br, AnalysisDeclContext* ac) :
|
||||
BR(br), AC(ac) {
|
||||
}
|
||||
WalkAST(const CheckerBase *checker, BugReporter &br, AnalysisDeclContext *ac)
|
||||
: Checker(checker), BR(br), AC(ac) {}
|
||||
|
||||
// Statement visitor methods.
|
||||
void VisitChildren(Stmt *S);
|
||||
@ -157,8 +157,9 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {
|
||||
os << "U";
|
||||
os << "se a safer 'strlcat' API";
|
||||
|
||||
BR.EmitBasicReport(FD, "Anti-pattern in the argument", "C String API",
|
||||
os.str(), Loc, LenArg->getSourceRange());
|
||||
BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument",
|
||||
"C String API", os.str(), Loc,
|
||||
LenArg->getSourceRange());
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,7 +180,7 @@ public:
|
||||
|
||||
void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
|
||||
BugReporter &BR) const {
|
||||
WalkAST walker(BR, Mgr.getAnalysisDeclContext(D));
|
||||
WalkAST walker(this, BR, Mgr.getAnalysisDeclContext(D));
|
||||
walker.Visit(D->getBody());
|
||||
}
|
||||
};
|
||||
|
@ -52,10 +52,10 @@ public:
|
||||
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
|
||||
|
||||
private:
|
||||
static bool PreVisitProcessArg(CheckerContext &C, SVal V,
|
||||
SourceRange argRange, const Expr *argEx,
|
||||
bool IsFirstArgument, bool checkUninitFields,
|
||||
const CallEvent &Call, OwningPtr<BugType> &BT);
|
||||
bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange argRange,
|
||||
const Expr *argEx, bool IsFirstArgument,
|
||||
bool checkUninitFields, const CallEvent &Call,
|
||||
OwningPtr<BugType> &BT) const;
|
||||
|
||||
static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE);
|
||||
void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg,
|
||||
@ -65,9 +65,9 @@ private:
|
||||
ProgramStateRef state,
|
||||
const ObjCMethodCall &msg) const;
|
||||
|
||||
static void LazyInit_BT(const char *desc, OwningPtr<BugType> &BT) {
|
||||
void LazyInit_BT(const char *desc, OwningPtr<BugType> &BT) const {
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug(desc));
|
||||
BT.reset(new BuiltinBug(this, desc));
|
||||
}
|
||||
};
|
||||
} // end anonymous namespace
|
||||
@ -119,7 +119,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
|
||||
bool IsFirstArgument,
|
||||
bool checkUninitFields,
|
||||
const CallEvent &Call,
|
||||
OwningPtr<BugType> &BT) {
|
||||
OwningPtr<BugType> &BT) const {
|
||||
if (V.isUndef()) {
|
||||
if (ExplodedNode *N = C.generateSink()) {
|
||||
LazyInit_BT("Uninitialized argument value", BT);
|
||||
@ -234,8 +234,8 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE,
|
||||
|
||||
if (L.isUndef()) {
|
||||
if (!BT_call_undef)
|
||||
BT_call_undef.reset(new BuiltinBug("Called function pointer is an "
|
||||
"uninitalized pointer value"));
|
||||
BT_call_undef.reset(new BuiltinBug(
|
||||
this, "Called function pointer is an uninitalized pointer value"));
|
||||
emitBadCall(BT_call_undef.get(), C, Callee);
|
||||
return;
|
||||
}
|
||||
@ -246,8 +246,8 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE,
|
||||
|
||||
if (StNull && !StNonNull) {
|
||||
if (!BT_call_null)
|
||||
BT_call_null.reset(
|
||||
new BuiltinBug("Called function pointer is null (null dereference)"));
|
||||
BT_call_null.reset(new BuiltinBug(
|
||||
this, "Called function pointer is null (null dereference)"));
|
||||
emitBadCall(BT_call_null.get(), C, Callee);
|
||||
return;
|
||||
}
|
||||
@ -265,7 +265,8 @@ void CallAndMessageChecker::checkPreStmt(const CXXDeleteExpr *DE,
|
||||
if (!N)
|
||||
return;
|
||||
if (!BT_cxx_delete_undef)
|
||||
BT_cxx_delete_undef.reset(new BuiltinBug("Uninitialized argument value"));
|
||||
BT_cxx_delete_undef.reset(
|
||||
new BuiltinBug(this, "Uninitialized argument value"));
|
||||
if (DE->isArrayFormAsWritten())
|
||||
Desc = "Argument to 'delete[]' is uninitialized";
|
||||
else
|
||||
@ -289,8 +290,8 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
|
||||
SVal V = CC->getCXXThisVal();
|
||||
if (V.isUndef()) {
|
||||
if (!BT_cxx_call_undef)
|
||||
BT_cxx_call_undef.reset(new BuiltinBug("Called C++ object pointer is "
|
||||
"uninitialized"));
|
||||
BT_cxx_call_undef.reset(
|
||||
new BuiltinBug(this, "Called C++ object pointer is uninitialized"));
|
||||
emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr());
|
||||
return;
|
||||
}
|
||||
@ -301,8 +302,8 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
|
||||
|
||||
if (StNull && !StNonNull) {
|
||||
if (!BT_cxx_call_null)
|
||||
BT_cxx_call_null.reset(new BuiltinBug("Called C++ object pointer "
|
||||
"is null"));
|
||||
BT_cxx_call_null.reset(
|
||||
new BuiltinBug(this, "Called C++ object pointer is null"));
|
||||
emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr());
|
||||
return;
|
||||
}
|
||||
@ -365,22 +366,21 @@ void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
||||
switch (msg.getMessageKind()) {
|
||||
case OCM_Message:
|
||||
if (!BT_msg_undef)
|
||||
BT_msg_undef.reset(new BuiltinBug("Receiver in message expression "
|
||||
BT_msg_undef.reset(new BuiltinBug(this,
|
||||
"Receiver in message expression "
|
||||
"is an uninitialized value"));
|
||||
BT = BT_msg_undef.get();
|
||||
break;
|
||||
case OCM_PropertyAccess:
|
||||
if (!BT_objc_prop_undef)
|
||||
BT_objc_prop_undef.reset(new BuiltinBug("Property access on an "
|
||||
"uninitialized object "
|
||||
"pointer"));
|
||||
BT_objc_prop_undef.reset(new BuiltinBug(
|
||||
this, "Property access on an uninitialized object pointer"));
|
||||
BT = BT_objc_prop_undef.get();
|
||||
break;
|
||||
case OCM_Subscript:
|
||||
if (!BT_objc_subscript_undef)
|
||||
BT_objc_subscript_undef.reset(new BuiltinBug("Subscript access on an "
|
||||
"uninitialized object "
|
||||
"pointer"));
|
||||
BT_objc_subscript_undef.reset(new BuiltinBug(
|
||||
this, "Subscript access on an uninitialized object pointer"));
|
||||
BT = BT_objc_subscript_undef.get();
|
||||
break;
|
||||
}
|
||||
@ -418,7 +418,7 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
|
||||
|
||||
if (!BT_msg_ret)
|
||||
BT_msg_ret.reset(
|
||||
new BuiltinBug("Receiver in message expression is 'nil'"));
|
||||
new BuiltinBug(this, "Receiver in message expression is 'nil'"));
|
||||
|
||||
const ObjCMessageExpr *ME = msg.getOriginExpr();
|
||||
|
||||
|
@ -69,9 +69,10 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
|
||||
if (regionSize % typeSize != 0) {
|
||||
if (ExplodedNode *errorNode = C.generateSink()) {
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug("Cast region with wrong size.",
|
||||
"Cast a region whose size is not a multiple of the"
|
||||
" destination type size."));
|
||||
BT.reset(
|
||||
new BuiltinBug(this, "Cast region with wrong size.",
|
||||
"Cast a region whose size is not a multiple of the"
|
||||
" destination type size."));
|
||||
BugReport *R = new BugReport(*BT, BT->getDescription(),
|
||||
errorNode);
|
||||
R->addRange(CE->getSourceRange());
|
||||
@ -80,7 +81,6 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ento::registerCastSizeChecker(CheckerManager &mgr) {
|
||||
mgr.registerChecker<CastSizeChecker>();
|
||||
mgr.registerChecker<CastSizeChecker>();
|
||||
}
|
||||
|
@ -58,10 +58,11 @@ void CastToStructChecker::checkPreStmt(const CastExpr *CE,
|
||||
if (!OrigPointeeTy->isRecordType()) {
|
||||
if (ExplodedNode *N = C.addTransition()) {
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug("Cast from non-struct type to struct type",
|
||||
"Casting a non-structure type to a structure type "
|
||||
"and accessing a field can lead to memory access "
|
||||
"errors or data corruption."));
|
||||
BT.reset(
|
||||
new BuiltinBug(this, "Cast from non-struct type to struct type",
|
||||
"Casting a non-structure type to a structure type "
|
||||
"and accessing a field can lead to memory access "
|
||||
"errors or data corruption."));
|
||||
BugReport *R = new BugReport(*BT,BT->getDescription(), N);
|
||||
R->addRange(CE->getSourceRange());
|
||||
C.emitReport(R);
|
||||
|
@ -97,8 +97,9 @@ static bool scan_ivar_release(Stmt *S, ObjCIvarDecl *ID,
|
||||
return false;
|
||||
}
|
||||
|
||||
static void checkObjCDealloc(const ObjCImplementationDecl *D,
|
||||
const LangOptions& LOpts, BugReporter& BR) {
|
||||
static void checkObjCDealloc(const CheckerBase *Checker,
|
||||
const ObjCImplementationDecl *D,
|
||||
const LangOptions &LOpts, BugReporter &BR) {
|
||||
|
||||
assert (LOpts.getGC() != LangOptions::GCOnly);
|
||||
|
||||
@ -180,7 +181,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D,
|
||||
llvm::raw_string_ostream os(buf);
|
||||
os << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method";
|
||||
|
||||
BR.EmitBasicReport(D, name, categories::CoreFoundationObjectiveC,
|
||||
BR.EmitBasicReport(D, Checker, name, categories::CoreFoundationObjectiveC,
|
||||
os.str(), DLoc);
|
||||
return;
|
||||
}
|
||||
@ -198,7 +199,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D,
|
||||
<< "' does not send a 'dealloc' message to its super class"
|
||||
" (missing [super dealloc])";
|
||||
|
||||
BR.EmitBasicReport(MD, name, categories::CoreFoundationObjectiveC,
|
||||
BR.EmitBasicReport(MD, Checker, name, categories::CoreFoundationObjectiveC,
|
||||
os.str(), DLoc);
|
||||
return;
|
||||
}
|
||||
@ -264,8 +265,8 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D,
|
||||
PathDiagnosticLocation SDLoc =
|
||||
PathDiagnosticLocation::createBegin(*I, BR.getSourceManager());
|
||||
|
||||
BR.EmitBasicReport(MD, name, categories::CoreFoundationObjectiveC,
|
||||
os.str(), SDLoc);
|
||||
BR.EmitBasicReport(MD, Checker, name,
|
||||
categories::CoreFoundationObjectiveC, os.str(), SDLoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -282,7 +283,8 @@ public:
|
||||
BugReporter &BR) const {
|
||||
if (mgr.getLangOpts().getGC() == LangOptions::GCOnly)
|
||||
return;
|
||||
checkObjCDealloc(cast<ObjCImplementationDecl>(D), mgr.getLangOpts(), BR);
|
||||
checkObjCDealloc(this, cast<ObjCImplementationDecl>(D), mgr.getLangOpts(),
|
||||
BR);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -40,7 +40,8 @@ static bool AreTypesCompatible(QualType Derived, QualType Ancestor,
|
||||
static void CompareReturnTypes(const ObjCMethodDecl *MethDerived,
|
||||
const ObjCMethodDecl *MethAncestor,
|
||||
BugReporter &BR, ASTContext &Ctx,
|
||||
const ObjCImplementationDecl *ID) {
|
||||
const ObjCImplementationDecl *ID,
|
||||
const CheckerBase *Checker) {
|
||||
|
||||
QualType ResDerived = MethDerived->getReturnType();
|
||||
QualType ResAncestor = MethAncestor->getReturnType();
|
||||
@ -69,15 +70,15 @@ static void CompareReturnTypes(const ObjCMethodDecl *MethDerived,
|
||||
PathDiagnosticLocation::createBegin(MethDerived,
|
||||
BR.getSourceManager());
|
||||
|
||||
BR.EmitBasicReport(MethDerived,
|
||||
"Incompatible instance method return type",
|
||||
categories::CoreFoundationObjectiveC,
|
||||
os.str(), MethDLoc);
|
||||
BR.EmitBasicReport(
|
||||
MethDerived, Checker, "Incompatible instance method return type",
|
||||
categories::CoreFoundationObjectiveC, os.str(), MethDLoc);
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID,
|
||||
BugReporter& BR) {
|
||||
BugReporter &BR,
|
||||
const CheckerBase *Checker) {
|
||||
|
||||
const ObjCInterfaceDecl *D = ID->getClassInterface();
|
||||
const ObjCInterfaceDecl *C = D->getSuperClass();
|
||||
@ -118,7 +119,7 @@ static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID,
|
||||
ObjCMethodDecl *MethDerived = MI->second;
|
||||
MI->second = 0;
|
||||
|
||||
CompareReturnTypes(MethDerived, M, BR, Ctx, ID);
|
||||
CompareReturnTypes(MethDerived, M, BR, Ctx, ID, Checker);
|
||||
}
|
||||
|
||||
C = C->getSuperClass();
|
||||
@ -135,7 +136,7 @@ class ObjCMethSigsChecker : public Checker<
|
||||
public:
|
||||
void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
|
||||
BugReporter &BR) const {
|
||||
CheckObjCInstMethSignature(D, BR);
|
||||
CheckObjCInstMethSignature(D, BR, this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -46,8 +46,18 @@ struct ChecksFilter {
|
||||
DefaultBool check_vfork;
|
||||
DefaultBool check_FloatLoopCounter;
|
||||
DefaultBool check_UncheckedReturn;
|
||||
|
||||
CheckName checkName_gets;
|
||||
CheckName checkName_getpw;
|
||||
CheckName checkName_mktemp;
|
||||
CheckName checkName_mkstemp;
|
||||
CheckName checkName_strcpy;
|
||||
CheckName checkName_rand;
|
||||
CheckName checkName_vfork;
|
||||
CheckName checkName_FloatLoopCounter;
|
||||
CheckName checkName_UncheckedReturn;
|
||||
};
|
||||
|
||||
|
||||
class WalkAST : public StmtVisitor<WalkAST> {
|
||||
BugReporter &BR;
|
||||
AnalysisDeclContext* AC;
|
||||
@ -281,7 +291,7 @@ void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
|
||||
|
||||
PathDiagnosticLocation FSLoc =
|
||||
PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);
|
||||
BR.EmitBasicReport(AC->getDecl(),
|
||||
BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
|
||||
bugType, "Security", os.str(),
|
||||
FSLoc, ranges);
|
||||
}
|
||||
@ -316,7 +326,7 @@ void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {
|
||||
// Issue a warning.
|
||||
PathDiagnosticLocation CELoc =
|
||||
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
|
||||
BR.EmitBasicReport(AC->getDecl(),
|
||||
BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
|
||||
"Potential buffer overflow in call to 'gets'",
|
||||
"Security",
|
||||
"Call to function 'gets' is extremely insecure as it can "
|
||||
@ -356,7 +366,7 @@ void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {
|
||||
// Issue a warning.
|
||||
PathDiagnosticLocation CELoc =
|
||||
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
|
||||
BR.EmitBasicReport(AC->getDecl(),
|
||||
BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
|
||||
"Potential buffer overflow in call to 'getpw'",
|
||||
"Security",
|
||||
"The getpw() function is dangerous as it may overflow the "
|
||||
@ -397,7 +407,7 @@ void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {
|
||||
// Issue a waring.
|
||||
PathDiagnosticLocation CELoc =
|
||||
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
|
||||
BR.EmitBasicReport(AC->getDecl(),
|
||||
BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
|
||||
"Potential insecure temporary file in call 'mktemp'",
|
||||
"Security",
|
||||
"Call to function 'mktemp' is insecure as it always "
|
||||
@ -483,7 +493,7 @@ void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {
|
||||
out << " used as a suffix";
|
||||
}
|
||||
out << ')';
|
||||
BR.EmitBasicReport(AC->getDecl(),
|
||||
BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
|
||||
"Insecure temporary file creation", "Security",
|
||||
out.str(), CELoc, strArg->getSourceRange());
|
||||
}
|
||||
@ -504,7 +514,7 @@ void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
|
||||
// Issue a warning.
|
||||
PathDiagnosticLocation CELoc =
|
||||
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
|
||||
BR.EmitBasicReport(AC->getDecl(),
|
||||
BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
|
||||
"Potential insecure memory buffer bounds restriction in "
|
||||
"call 'strcpy'",
|
||||
"Security",
|
||||
@ -531,7 +541,7 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
|
||||
// Issue a warning.
|
||||
PathDiagnosticLocation CELoc =
|
||||
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
|
||||
BR.EmitBasicReport(AC->getDecl(),
|
||||
BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
|
||||
"Potential insecure memory buffer bounds restriction in "
|
||||
"call 'strcat'",
|
||||
"Security",
|
||||
@ -609,8 +619,9 @@ void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
|
||||
|
||||
PathDiagnosticLocation CELoc =
|
||||
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
|
||||
BR.EmitBasicReport(AC->getDecl(), os1.str(), "Security", os2.str(),
|
||||
CELoc, CE->getCallee()->getSourceRange());
|
||||
BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
|
||||
"Security", os2.str(), CELoc,
|
||||
CE->getCallee()->getSourceRange());
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -633,7 +644,7 @@ void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {
|
||||
// Issue a warning.
|
||||
PathDiagnosticLocation CELoc =
|
||||
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
|
||||
BR.EmitBasicReport(AC->getDecl(),
|
||||
BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
|
||||
"'random' is not a secure random number generator",
|
||||
"Security",
|
||||
"The 'random' function produces a sequence of values that "
|
||||
@ -653,7 +664,7 @@ void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
|
||||
// All calls to vfork() are insecure, issue a warning.
|
||||
PathDiagnosticLocation CELoc =
|
||||
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
|
||||
BR.EmitBasicReport(AC->getDecl(),
|
||||
BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
|
||||
"Potential insecure implementation-specific behavior in "
|
||||
"call 'vfork'",
|
||||
"Security",
|
||||
@ -724,8 +735,9 @@ void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
|
||||
|
||||
PathDiagnosticLocation CELoc =
|
||||
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
|
||||
BR.EmitBasicReport(AC->getDecl(), os1.str(), "Security", os2.str(),
|
||||
CELoc, CE->getCallee()->getSourceRange());
|
||||
BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
|
||||
"Security", os2.str(), CELoc,
|
||||
CE->getCallee()->getSourceRange());
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -745,10 +757,13 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
#define REGISTER_CHECKER(name) \
|
||||
void ento::register##name(CheckerManager &mgr) {\
|
||||
mgr.registerChecker<SecuritySyntaxChecker>()->filter.check_##name = true;\
|
||||
}
|
||||
#define REGISTER_CHECKER(name) \
|
||||
void ento::register##name(CheckerManager &mgr) { \
|
||||
SecuritySyntaxChecker *checker = \
|
||||
mgr.registerChecker<SecuritySyntaxChecker>(); \
|
||||
checker->filter.check_##name = true; \
|
||||
checker->filter.checkName_##name = mgr.getCurrentCheckName(); \
|
||||
}
|
||||
|
||||
REGISTER_CHECKER(gets)
|
||||
REGISTER_CHECKER(getpw)
|
||||
|
@ -24,10 +24,12 @@ using namespace ento;
|
||||
namespace {
|
||||
class WalkAST : public StmtVisitor<WalkAST> {
|
||||
BugReporter &BR;
|
||||
const CheckerBase *Checker;
|
||||
AnalysisDeclContext* AC;
|
||||
|
||||
public:
|
||||
WalkAST(BugReporter &br, AnalysisDeclContext* ac) : BR(br), AC(ac) {}
|
||||
WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)
|
||||
: BR(br), Checker(checker), AC(ac) {}
|
||||
void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E);
|
||||
void VisitStmt(Stmt *S) { VisitChildren(S); }
|
||||
void VisitChildren(Stmt *S);
|
||||
@ -62,7 +64,7 @@ void WalkAST::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) {
|
||||
|
||||
PathDiagnosticLocation ELoc =
|
||||
PathDiagnosticLocation::createBegin(E, BR.getSourceManager(), AC);
|
||||
BR.EmitBasicReport(AC->getDecl(),
|
||||
BR.EmitBasicReport(AC->getDecl(), Checker,
|
||||
"Potential unintended use of sizeof() on pointer type",
|
||||
categories::LogicError,
|
||||
"The code calls sizeof() on a pointer type. "
|
||||
@ -80,7 +82,7 @@ class SizeofPointerChecker : public Checker<check::ASTCodeBody> {
|
||||
public:
|
||||
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
|
||||
BugReporter &BR) const {
|
||||
WalkAST walker(BR, mgr.getAnalysisDeclContext(D));
|
||||
WalkAST walker(BR, this, mgr.getAnalysisDeclContext(D));
|
||||
walker.Visit(D->getBody());
|
||||
}
|
||||
};
|
||||
|
@ -142,9 +142,9 @@ void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
|
||||
if (isRootChanged((intptr_t) *k))
|
||||
if (ExplodedNode *N = C.addTransition()) {
|
||||
if (!BT_BreakJail)
|
||||
BT_BreakJail.reset(new BuiltinBug("Break out of jail",
|
||||
"No call of chdir(\"/\") immediately "
|
||||
"after chroot"));
|
||||
BT_BreakJail.reset(new BuiltinBug(
|
||||
this, "Break out of jail", "No call of chdir(\"/\") immediately "
|
||||
"after chroot"));
|
||||
BugReport *R = new BugReport(*BT_BreakJail,
|
||||
BT_BreakJail->getDescription(), N);
|
||||
C.emitReport(R);
|
||||
|
@ -124,6 +124,7 @@ class DeadStoreObs : public LiveVariables::Observer {
|
||||
const CFG &cfg;
|
||||
ASTContext &Ctx;
|
||||
BugReporter& BR;
|
||||
const CheckerBase *Checker;
|
||||
AnalysisDeclContext* AC;
|
||||
ParentMap& Parents;
|
||||
llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
|
||||
@ -134,11 +135,12 @@ class DeadStoreObs : public LiveVariables::Observer {
|
||||
enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit };
|
||||
|
||||
public:
|
||||
DeadStoreObs(const CFG &cfg, ASTContext &ctx,
|
||||
BugReporter& br, AnalysisDeclContext* ac, ParentMap& parents,
|
||||
llvm::SmallPtrSet<const VarDecl*, 20> &escaped)
|
||||
: cfg(cfg), Ctx(ctx), BR(br), AC(ac), Parents(parents),
|
||||
Escaped(escaped), currentBlock(0) {}
|
||||
DeadStoreObs(const CFG &cfg, ASTContext &ctx, BugReporter &br,
|
||||
const CheckerBase *checker, AnalysisDeclContext *ac,
|
||||
ParentMap &parents,
|
||||
llvm::SmallPtrSet<const VarDecl *, 20> &escaped)
|
||||
: cfg(cfg), Ctx(ctx), BR(br), Checker(checker), AC(ac), Parents(parents),
|
||||
Escaped(escaped), currentBlock(0) {}
|
||||
|
||||
virtual ~DeadStoreObs() {}
|
||||
|
||||
@ -199,7 +201,8 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
BR.EmitBasicReport(AC->getDecl(), BugType, "Dead store", os.str(), L, R);
|
||||
BR.EmitBasicReport(AC->getDecl(), Checker, BugType, "Dead store", os.str(),
|
||||
L, R);
|
||||
}
|
||||
|
||||
void CheckVarDecl(const VarDecl *VD, const Expr *Ex, const Expr *Val,
|
||||
@ -439,7 +442,7 @@ public:
|
||||
ParentMap &pmap = mgr.getParentMap(D);
|
||||
FindEscaped FS;
|
||||
cfg.VisitBlockStmts(FS);
|
||||
DeadStoreObs A(cfg, BR.getContext(), BR, AC, pmap, FS.Escaped);
|
||||
DeadStoreObs A(cfg, BR.getContext(), BR, this, AC, pmap, FS.Escaped);
|
||||
L->runOnAllBlocks(A);
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
|
||||
// We know that 'location' cannot be non-null. This is what
|
||||
// we call an "explicit" null dereference.
|
||||
if (!BT_null)
|
||||
BT_null.reset(new BuiltinBug("Dereference of null pointer"));
|
||||
BT_null.reset(new BuiltinBug(this, "Dereference of null pointer"));
|
||||
|
||||
SmallString<100> buf;
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
@ -180,7 +180,8 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
|
||||
if (l.isUndef()) {
|
||||
if (ExplodedNode *N = C.generateSink()) {
|
||||
if (!BT_undef)
|
||||
BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value"));
|
||||
BT_undef.reset(
|
||||
new BuiltinBug(this, "Dereference of undefined pointer value"));
|
||||
|
||||
BugReport *report =
|
||||
new BugReport(*BT_undef, BT_undef->getDescription(), N);
|
||||
|
@ -63,13 +63,15 @@ class DirectIvarAssignment :
|
||||
const ObjCMethodDecl *MD;
|
||||
const ObjCInterfaceDecl *InterfD;
|
||||
BugReporter &BR;
|
||||
const CheckerBase *Checker;
|
||||
LocationOrAnalysisDeclContext DCtx;
|
||||
|
||||
public:
|
||||
MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD,
|
||||
const ObjCInterfaceDecl *InID,
|
||||
BugReporter &InBR, AnalysisDeclContext *InDCtx)
|
||||
: IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR), DCtx(InDCtx) {}
|
||||
const ObjCInterfaceDecl *InID, BugReporter &InBR,
|
||||
const CheckerBase *Checker, AnalysisDeclContext *InDCtx)
|
||||
: IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR),
|
||||
Checker(Checker), DCtx(InDCtx) {}
|
||||
|
||||
void VisitStmt(const Stmt *S) { VisitChildren(S); }
|
||||
|
||||
@ -152,7 +154,8 @@ void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D,
|
||||
const Stmt *Body = M->getBody();
|
||||
assert(Body);
|
||||
|
||||
MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, DCtx);
|
||||
MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, this,
|
||||
DCtx);
|
||||
MC.VisitStmt(Body);
|
||||
}
|
||||
}
|
||||
@ -204,13 +207,11 @@ void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator(
|
||||
if (GetterMethod && GetterMethod->getCanonicalDecl() == MD)
|
||||
return;
|
||||
|
||||
BR.EmitBasicReport(MD,
|
||||
"Property access",
|
||||
categories::CoreFoundationObjectiveC,
|
||||
BR.EmitBasicReport(
|
||||
MD, Checker, "Property access", categories::CoreFoundationObjectiveC,
|
||||
"Direct assignment to an instance variable backing a property; "
|
||||
"use the setter instead", PathDiagnosticLocation(IvarRef,
|
||||
BR.getSourceManager(),
|
||||
DCtx));
|
||||
"use the setter instead",
|
||||
PathDiagnosticLocation(IvarRef, BR.getSourceManager(), DCtx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ void DivZeroChecker::reportBug(const char *Msg,
|
||||
CheckerContext &C) const {
|
||||
if (ExplodedNode *N = C.generateSink(StateZero)) {
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug("Division by zero"));
|
||||
BT.reset(new BuiltinBug(this, "Division by zero"));
|
||||
|
||||
BugReport *R = new BugReport(*BT, Msg, N);
|
||||
bugreporter::trackNullOrUndefValue(N, bugreporter::GetDenomExpr(N), *R);
|
||||
|
@ -95,7 +95,7 @@ void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
|
||||
return;
|
||||
|
||||
if (!BT)
|
||||
BT.reset(new BugType("Checking analyzer assumptions", "debug"));
|
||||
BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
|
||||
|
||||
BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N);
|
||||
C.emitReport(R);
|
||||
@ -106,7 +106,7 @@ void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE,
|
||||
ExplodedNode *N = C.getPredecessor();
|
||||
|
||||
if (!BT)
|
||||
BT.reset(new BugType("Checking analyzer assumptions", "debug"));
|
||||
BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
|
||||
|
||||
BugReport *R = new BugReport(*BT, "REACHABLE", N);
|
||||
C.emitReport(R);
|
||||
@ -126,7 +126,7 @@ void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
|
||||
return;
|
||||
|
||||
if (!BT)
|
||||
BT.reset(new BugType("Checking analyzer assumptions", "debug"));
|
||||
BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
|
||||
|
||||
BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N);
|
||||
C.emitReport(R);
|
||||
|
@ -52,10 +52,11 @@ void FixedAddressChecker::checkPreStmt(const BinaryOperator *B,
|
||||
|
||||
if (ExplodedNode *N = C.addTransition()) {
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug("Use fixed address",
|
||||
"Using a fixed address is not portable because that "
|
||||
"address will probably not be valid in all "
|
||||
"environments or platforms."));
|
||||
BT.reset(
|
||||
new BuiltinBug(this, "Use fixed address",
|
||||
"Using a fixed address is not portable because that "
|
||||
"address will probably not be valid in all "
|
||||
"environments or platforms."));
|
||||
BugReport *R = new BugReport(*BT, BT->getDescription(), N);
|
||||
R->addRange(B->getRHS()->getSourceRange());
|
||||
C.emitReport(R);
|
||||
|
@ -46,7 +46,7 @@ private:
|
||||
mutable OwningPtr<BugType> BT;
|
||||
inline void initBugType() const {
|
||||
if (!BT)
|
||||
BT.reset(new BugType("Use of Untrusted Data", "Untrusted Data"));
|
||||
BT.reset(new BugType(this, "Use of Untrusted Data", "Untrusted Data"));
|
||||
}
|
||||
|
||||
/// \brief Catch taint related bugs. Check if tainted data is passed to a
|
||||
|
@ -36,8 +36,10 @@ namespace {
|
||||
class FindIdenticalExprVisitor
|
||||
: public RecursiveASTVisitor<FindIdenticalExprVisitor> {
|
||||
public:
|
||||
explicit FindIdenticalExprVisitor(BugReporter &B, AnalysisDeclContext *A)
|
||||
: BR(B), AC(A) {}
|
||||
explicit FindIdenticalExprVisitor(BugReporter &B,
|
||||
const CheckerBase *Checker,
|
||||
AnalysisDeclContext *A)
|
||||
: BR(B), Checker(Checker), AC(A) {}
|
||||
// FindIdenticalExprVisitor only visits nodes
|
||||
// that are binary operators or conditional operators.
|
||||
bool VisitBinaryOperator(const BinaryOperator *B);
|
||||
@ -45,6 +47,7 @@ public:
|
||||
|
||||
private:
|
||||
BugReporter &BR;
|
||||
const CheckerBase *Checker;
|
||||
AnalysisDeclContext *AC;
|
||||
};
|
||||
} // end anonymous namespace
|
||||
@ -112,7 +115,8 @@ bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) {
|
||||
Message = "comparison of identical expressions always evaluates to true";
|
||||
else
|
||||
Message = "comparison of identical expressions always evaluates to false";
|
||||
BR.EmitBasicReport(AC->getDecl(), "Compare of identical expressions",
|
||||
BR.EmitBasicReport(AC->getDecl(), Checker,
|
||||
"Compare of identical expressions",
|
||||
categories::LogicError, Message, ELoc);
|
||||
}
|
||||
// We want to visit ALL nodes (subexpressions of binary comparison
|
||||
@ -137,7 +141,8 @@ bool FindIdenticalExprVisitor::VisitConditionalOperator(
|
||||
Sr[0] = C->getTrueExpr()->getSourceRange();
|
||||
Sr[1] = C->getFalseExpr()->getSourceRange();
|
||||
BR.EmitBasicReport(
|
||||
AC->getDecl(), "Identical expressions in conditional expression",
|
||||
AC->getDecl(), Checker,
|
||||
"Identical expressions in conditional expression",
|
||||
categories::LogicError,
|
||||
"identical expressions on both sides of ':' in conditional expression",
|
||||
ELoc, Sr);
|
||||
@ -245,7 +250,7 @@ class FindIdenticalExprChecker : public Checker<check::ASTCodeBody> {
|
||||
public:
|
||||
void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
|
||||
BugReporter &BR) const {
|
||||
FindIdenticalExprVisitor Visitor(BR, Mgr.getAnalysisDeclContext(D));
|
||||
FindIdenticalExprVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D));
|
||||
Visitor.TraverseDecl(const_cast<Decl *>(D));
|
||||
}
|
||||
};
|
||||
|
@ -49,6 +49,9 @@ struct ChecksFilter {
|
||||
DefaultBool check_MissingInvalidationMethod;
|
||||
/// Check that all ivars are invalidated.
|
||||
DefaultBool check_InstanceVariableInvalidation;
|
||||
|
||||
CheckName checkName_MissingInvalidationMethod;
|
||||
CheckName checkName_InstanceVariableInvalidation;
|
||||
};
|
||||
|
||||
class IvarInvalidationCheckerImpl {
|
||||
@ -200,7 +203,8 @@ class IvarInvalidationCheckerImpl {
|
||||
const ObjCIvarDecl *IvarDecl,
|
||||
const IvarToPropMapTy &IvarToPopertyMap);
|
||||
|
||||
void reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl,
|
||||
void reportNoInvalidationMethod(CheckName CheckName,
|
||||
const ObjCIvarDecl *FirstIvarDecl,
|
||||
const IvarToPropMapTy &IvarToPopertyMap,
|
||||
const ObjCInterfaceDecl *InterfaceD,
|
||||
bool MissingDeclaration) const;
|
||||
@ -476,7 +480,8 @@ visit(const ObjCImplementationDecl *ImplD) const {
|
||||
// Report an error in case none of the invalidation methods are declared.
|
||||
if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) {
|
||||
if (Filter.check_MissingInvalidationMethod)
|
||||
reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD,
|
||||
reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod,
|
||||
FirstIvarDecl, IvarToPopertyMap, InterfaceD,
|
||||
/*MissingDeclaration*/ true);
|
||||
// If there are no invalidation methods, there is no ivar validation work
|
||||
// to be done.
|
||||
@ -532,17 +537,17 @@ visit(const ObjCImplementationDecl *ImplD) const {
|
||||
reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, 0);
|
||||
} else {
|
||||
// Otherwise, no invalidation methods were implemented.
|
||||
reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD,
|
||||
reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation,
|
||||
FirstIvarDecl, IvarToPopertyMap, InterfaceD,
|
||||
/*MissingDeclaration*/ false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IvarInvalidationCheckerImpl::
|
||||
reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl,
|
||||
const IvarToPropMapTy &IvarToPopertyMap,
|
||||
const ObjCInterfaceDecl *InterfaceD,
|
||||
bool MissingDeclaration) const {
|
||||
void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
|
||||
CheckName CheckName, const ObjCIvarDecl *FirstIvarDecl,
|
||||
const IvarToPropMapTy &IvarToPopertyMap,
|
||||
const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const {
|
||||
SmallString<128> sbuf;
|
||||
llvm::raw_svector_ostream os(sbuf);
|
||||
assert(FirstIvarDecl);
|
||||
@ -557,7 +562,7 @@ reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl,
|
||||
PathDiagnosticLocation IvarDecLocation =
|
||||
PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager());
|
||||
|
||||
BR.EmitBasicReport(FirstIvarDecl, "Incomplete invalidation",
|
||||
BR.EmitBasicReport(FirstIvarDecl, CheckName, "Incomplete invalidation",
|
||||
categories::CoreFoundationObjectiveC, os.str(),
|
||||
IvarDecLocation);
|
||||
}
|
||||
@ -575,15 +580,16 @@ reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
|
||||
PathDiagnosticLocation::createEnd(MethodD->getBody(),
|
||||
BR.getSourceManager(),
|
||||
Mgr.getAnalysisDeclContext(MethodD));
|
||||
BR.EmitBasicReport(MethodD, "Incomplete invalidation",
|
||||
BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation,
|
||||
"Incomplete invalidation",
|
||||
categories::CoreFoundationObjectiveC, os.str(),
|
||||
MethodDecLocation);
|
||||
} else {
|
||||
BR.EmitBasicReport(IvarD, "Incomplete invalidation",
|
||||
categories::CoreFoundationObjectiveC, os.str(),
|
||||
PathDiagnosticLocation::createBegin(IvarD,
|
||||
BR.getSourceManager()));
|
||||
|
||||
BR.EmitBasicReport(
|
||||
IvarD, Filter.checkName_InstanceVariableInvalidation,
|
||||
"Incomplete invalidation", categories::CoreFoundationObjectiveC,
|
||||
os.str(),
|
||||
PathDiagnosticLocation::createBegin(IvarD, BR.getSourceManager()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -750,10 +756,13 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
#define REGISTER_CHECKER(name) \
|
||||
void ento::register##name(CheckerManager &mgr) {\
|
||||
mgr.registerChecker<IvarInvalidationChecker>()->Filter.check_##name = true;\
|
||||
}
|
||||
#define REGISTER_CHECKER(name) \
|
||||
void ento::register##name(CheckerManager &mgr) { \
|
||||
IvarInvalidationChecker *checker = \
|
||||
mgr.registerChecker<IvarInvalidationChecker>(); \
|
||||
checker->Filter.check_##name = true; \
|
||||
checker->Filter.checkName_##name = mgr.getCurrentCheckName(); \
|
||||
}
|
||||
|
||||
REGISTER_CHECKER(InstanceVariableInvalidation)
|
||||
REGISTER_CHECKER(MissingInvalidationMethod)
|
||||
|
@ -115,11 +115,14 @@ static bool IsSmallVector(QualType T) {
|
||||
|
||||
namespace {
|
||||
class StringRefCheckerVisitor : public StmtVisitor<StringRefCheckerVisitor> {
|
||||
BugReporter &BR;
|
||||
const Decl *DeclWithIssue;
|
||||
BugReporter &BR;
|
||||
const CheckerBase *Checker;
|
||||
|
||||
public:
|
||||
StringRefCheckerVisitor(const Decl *declWithIssue, BugReporter &br)
|
||||
: BR(br), DeclWithIssue(declWithIssue) {}
|
||||
StringRefCheckerVisitor(const Decl *declWithIssue, BugReporter &br,
|
||||
const CheckerBase *checker)
|
||||
: DeclWithIssue(declWithIssue), BR(br), Checker(checker) {}
|
||||
void VisitChildren(Stmt *S) {
|
||||
for (Stmt::child_iterator I = S->child_begin(), E = S->child_end() ;
|
||||
I != E; ++I)
|
||||
@ -133,8 +136,9 @@ private:
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR) {
|
||||
StringRefCheckerVisitor walker(D, BR);
|
||||
static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR,
|
||||
const CheckerBase *Checker) {
|
||||
StringRefCheckerVisitor walker(D, BR, Checker);
|
||||
walker.Visit(D->getBody());
|
||||
}
|
||||
|
||||
@ -179,7 +183,7 @@ void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) {
|
||||
"std::string that it outlives";
|
||||
PathDiagnosticLocation VDLoc =
|
||||
PathDiagnosticLocation::createBegin(VD, BR.getSourceManager());
|
||||
BR.EmitBasicReport(DeclWithIssue, desc, "LLVM Conventions", desc,
|
||||
BR.EmitBasicReport(DeclWithIssue, Checker, desc, "LLVM Conventions", desc,
|
||||
VDLoc, Init->getSourceRange());
|
||||
}
|
||||
|
||||
@ -216,22 +220,26 @@ class ASTFieldVisitor {
|
||||
SmallVector<FieldDecl*, 10> FieldChain;
|
||||
const CXXRecordDecl *Root;
|
||||
BugReporter &BR;
|
||||
const CheckerBase *Checker;
|
||||
|
||||
public:
|
||||
ASTFieldVisitor(const CXXRecordDecl *root, BugReporter &br)
|
||||
: Root(root), BR(br) {}
|
||||
ASTFieldVisitor(const CXXRecordDecl *root, BugReporter &br,
|
||||
const CheckerBase *checker)
|
||||
: Root(root), BR(br), Checker(checker) {}
|
||||
|
||||
void Visit(FieldDecl *D);
|
||||
void ReportError(QualType T);
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
static void CheckASTMemory(const CXXRecordDecl *R, BugReporter &BR) {
|
||||
static void CheckASTMemory(const CXXRecordDecl *R, BugReporter &BR,
|
||||
const CheckerBase *Checker) {
|
||||
if (!IsPartOfAST(R))
|
||||
return;
|
||||
|
||||
for (RecordDecl::field_iterator I = R->field_begin(), E = R->field_end();
|
||||
I != E; ++I) {
|
||||
ASTFieldVisitor walker(R, BR);
|
||||
ASTFieldVisitor walker(R, BR, Checker);
|
||||
walker.Visit(*I);
|
||||
}
|
||||
}
|
||||
@ -284,8 +292,8 @@ void ASTFieldVisitor::ReportError(QualType T) {
|
||||
// the class may be in the header file, for example).
|
||||
PathDiagnosticLocation L = PathDiagnosticLocation::createBegin(
|
||||
FieldChain.front(), BR.getSourceManager());
|
||||
BR.EmitBasicReport(Root, "AST node allocates heap memory", "LLVM Conventions",
|
||||
os.str(), L);
|
||||
BR.EmitBasicReport(Root, Checker, "AST node allocates heap memory",
|
||||
"LLVM Conventions", os.str(), L);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -300,12 +308,12 @@ public:
|
||||
void checkASTDecl(const CXXRecordDecl *R, AnalysisManager& mgr,
|
||||
BugReporter &BR) const {
|
||||
if (R->isCompleteDefinition())
|
||||
CheckASTMemory(R, BR);
|
||||
CheckASTMemory(R, BR, this);
|
||||
}
|
||||
|
||||
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
|
||||
BugReporter &BR) const {
|
||||
CheckStringRefAssignedTemporary(D, BR);
|
||||
CheckStringRefAssignedTemporary(D, BR, this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ private:
|
||||
|
||||
inline void initBugType() const {
|
||||
if (!BT)
|
||||
BT.reset(new BugType("Improper use of SecKeychain API",
|
||||
BT.reset(new BugType(this, "Improper use of SecKeychain API",
|
||||
"API Misuse (Apple)"));
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
|
||||
return;
|
||||
|
||||
if (!BT_dispatchOnce)
|
||||
BT_dispatchOnce.reset(new BugType("Improper use of 'dispatch_once'",
|
||||
BT_dispatchOnce.reset(new BugType(this, "Improper use of 'dispatch_once'",
|
||||
"API Misuse (Apple)"));
|
||||
|
||||
// Handle _dispatch_once. In some versions of the OS X SDK we have the case
|
||||
|
@ -155,31 +155,23 @@ class MallocChecker : public Checker<check::DeadSymbols,
|
||||
check::Location,
|
||||
eval::Assume>
|
||||
{
|
||||
mutable OwningPtr<BugType> BT_DoubleFree;
|
||||
mutable OwningPtr<BugType> BT_DoubleDelete;
|
||||
mutable OwningPtr<BugType> BT_Leak;
|
||||
mutable OwningPtr<BugType> BT_UseFree;
|
||||
mutable OwningPtr<BugType> BT_BadFree;
|
||||
mutable OwningPtr<BugType> BT_MismatchedDealloc;
|
||||
mutable OwningPtr<BugType> BT_OffsetFree;
|
||||
mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc,
|
||||
*II_valloc, *II_reallocf, *II_strndup, *II_strdup;
|
||||
|
||||
public:
|
||||
MallocChecker() : II_malloc(0), II_free(0), II_realloc(0), II_calloc(0),
|
||||
II_valloc(0), II_reallocf(0), II_strndup(0), II_strdup(0) {}
|
||||
|
||||
/// In pessimistic mode, the checker assumes that it does not know which
|
||||
/// functions might free the memory.
|
||||
struct ChecksFilter {
|
||||
DefaultBool CMallocPessimistic;
|
||||
DefaultBool CMallocOptimistic;
|
||||
DefaultBool CNewDeleteChecker;
|
||||
DefaultBool CNewDeleteLeaksChecker;
|
||||
DefaultBool CMismatchedDeallocatorChecker;
|
||||
enum CheckKind {
|
||||
CK_MallocPessimistic,
|
||||
CK_MallocOptimistic,
|
||||
CK_NewDeleteChecker,
|
||||
CK_NewDeleteLeaksChecker,
|
||||
CK_MismatchedDeallocatorChecker,
|
||||
CK_NumCheckKinds
|
||||
};
|
||||
|
||||
ChecksFilter Filter;
|
||||
DefaultBool ChecksEnabled[CK_NumCheckKinds];
|
||||
CheckName CheckNames[CK_NumCheckKinds];
|
||||
|
||||
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
|
||||
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
|
||||
@ -207,6 +199,16 @@ public:
|
||||
const char *NL, const char *Sep) const;
|
||||
|
||||
private:
|
||||
mutable OwningPtr<BugType> BT_DoubleFree[CK_NumCheckKinds];
|
||||
mutable OwningPtr<BugType> BT_DoubleDelete;
|
||||
mutable OwningPtr<BugType> BT_Leak[CK_NumCheckKinds];
|
||||
mutable OwningPtr<BugType> BT_UseFree[CK_NumCheckKinds];
|
||||
mutable OwningPtr<BugType> BT_BadFree[CK_NumCheckKinds];
|
||||
mutable OwningPtr<BugType> BT_MismatchedDealloc;
|
||||
mutable OwningPtr<BugType> BT_OffsetFree[CK_NumCheckKinds];
|
||||
mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc,
|
||||
*II_valloc, *II_reallocf, *II_strndup, *II_strdup;
|
||||
|
||||
void initIdentifierInfo(ASTContext &C) const;
|
||||
|
||||
/// \brief Determine family of a deallocation expression.
|
||||
@ -304,10 +306,12 @@ private:
|
||||
|
||||
///@{
|
||||
/// Tells if a given family/call/symbol is tracked by the current checker.
|
||||
bool isTrackedByCurrentChecker(AllocationFamily Family) const;
|
||||
bool isTrackedByCurrentChecker(CheckerContext &C,
|
||||
const Stmt *AllocDeallocStmt) const;
|
||||
bool isTrackedByCurrentChecker(CheckerContext &C, SymbolRef Sym) const;
|
||||
/// Sets CheckKind to the kind of the checker responsible for this
|
||||
/// family/call/symbol.
|
||||
Optional<CheckKind> getCheckIfTracked(AllocationFamily Family) const;
|
||||
Optional<CheckKind> getCheckIfTracked(CheckerContext &C,
|
||||
const Stmt *AllocDeallocStmt) const;
|
||||
Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym) const;
|
||||
///@}
|
||||
static bool SummarizeValue(raw_ostream &os, SVal V);
|
||||
static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR);
|
||||
@ -507,7 +511,7 @@ bool MallocChecker::isAllocationFunction(const FunctionDecl *FD,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Filter.CMallocOptimistic && FD->hasAttrs())
|
||||
if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs())
|
||||
for (specific_attr_iterator<OwnershipAttr>
|
||||
i = FD->specific_attr_begin<OwnershipAttr>(),
|
||||
e = FD->specific_attr_end<OwnershipAttr>();
|
||||
@ -529,7 +533,7 @@ bool MallocChecker::isFreeFunction(const FunctionDecl *FD, ASTContext &C) const
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Filter.CMallocOptimistic && FD->hasAttrs())
|
||||
if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs())
|
||||
for (specific_attr_iterator<OwnershipAttr>
|
||||
i = FD->specific_attr_begin<OwnershipAttr>(),
|
||||
e = FD->specific_attr_end<OwnershipAttr>();
|
||||
@ -624,7 +628,8 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
|
||||
}
|
||||
}
|
||||
|
||||
if (Filter.CMallocOptimistic || Filter.CMismatchedDeallocatorChecker) {
|
||||
if (ChecksEnabled[CK_MallocOptimistic] ||
|
||||
ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
|
||||
// Check all the attributes, if there are any.
|
||||
// There can be multiple of these attributes.
|
||||
if (FD->hasAttrs())
|
||||
@ -671,7 +676,7 @@ void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
|
||||
void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE,
|
||||
CheckerContext &C) const {
|
||||
|
||||
if (!Filter.CNewDeleteChecker)
|
||||
if (!ChecksEnabled[CK_NewDeleteChecker])
|
||||
if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol())
|
||||
checkUseAfterFree(Sym, C, DE->getArgument());
|
||||
|
||||
@ -1092,18 +1097,23 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
|
||||
RefState::getReleased(Family, ParentExpr));
|
||||
}
|
||||
|
||||
bool MallocChecker::isTrackedByCurrentChecker(AllocationFamily Family) const {
|
||||
Optional<MallocChecker::CheckKind>
|
||||
MallocChecker::getCheckIfTracked(AllocationFamily Family) const {
|
||||
switch (Family) {
|
||||
case AF_Malloc: {
|
||||
if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic)
|
||||
return false;
|
||||
return true;
|
||||
if (ChecksEnabled[CK_MallocOptimistic]) {
|
||||
return CK_MallocOptimistic;
|
||||
} else if (ChecksEnabled[CK_MallocPessimistic]) {
|
||||
return CK_MallocPessimistic;
|
||||
}
|
||||
return Optional<MallocChecker::CheckKind>();
|
||||
}
|
||||
case AF_CXXNew:
|
||||
case AF_CXXNewArray: {
|
||||
if (!Filter.CNewDeleteChecker)
|
||||
return false;
|
||||
return true;
|
||||
if (ChecksEnabled[CK_NewDeleteChecker]) {
|
||||
return CK_NewDeleteChecker;
|
||||
}
|
||||
return Optional<MallocChecker::CheckKind>();
|
||||
}
|
||||
case AF_None: {
|
||||
llvm_unreachable("no family");
|
||||
@ -1112,18 +1122,18 @@ bool MallocChecker::isTrackedByCurrentChecker(AllocationFamily Family) const {
|
||||
llvm_unreachable("unhandled family");
|
||||
}
|
||||
|
||||
bool
|
||||
MallocChecker::isTrackedByCurrentChecker(CheckerContext &C,
|
||||
const Stmt *AllocDeallocStmt) const {
|
||||
return isTrackedByCurrentChecker(getAllocationFamily(C, AllocDeallocStmt));
|
||||
Optional<MallocChecker::CheckKind>
|
||||
MallocChecker::getCheckIfTracked(CheckerContext &C,
|
||||
const Stmt *AllocDeallocStmt) const {
|
||||
return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt));
|
||||
}
|
||||
|
||||
bool MallocChecker::isTrackedByCurrentChecker(CheckerContext &C,
|
||||
SymbolRef Sym) const {
|
||||
Optional<MallocChecker::CheckKind>
|
||||
MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym) const {
|
||||
|
||||
const RefState *RS = C.getState()->get<RegionState>(Sym);
|
||||
assert(RS);
|
||||
return isTrackedByCurrentChecker(RS->getAllocationFamily());
|
||||
return getCheckIfTracked(RS->getAllocationFamily());
|
||||
}
|
||||
|
||||
bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {
|
||||
@ -1217,17 +1227,21 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
|
||||
SourceRange Range,
|
||||
const Expr *DeallocExpr) const {
|
||||
|
||||
if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic &&
|
||||
!Filter.CNewDeleteChecker)
|
||||
if (!ChecksEnabled[CK_MallocOptimistic] &&
|
||||
!ChecksEnabled[CK_MallocPessimistic] &&
|
||||
!ChecksEnabled[CK_NewDeleteChecker])
|
||||
return;
|
||||
|
||||
if (!isTrackedByCurrentChecker(C, DeallocExpr))
|
||||
Optional<MallocChecker::CheckKind> CheckKind =
|
||||
getCheckIfTracked(C, DeallocExpr);
|
||||
if (!CheckKind.hasValue())
|
||||
return;
|
||||
|
||||
if (ExplodedNode *N = C.generateSink()) {
|
||||
if (!BT_BadFree)
|
||||
BT_BadFree.reset(new BugType("Bad free", "Memory Error"));
|
||||
|
||||
if (!BT_BadFree[*CheckKind])
|
||||
BT_BadFree[*CheckKind].reset(
|
||||
new BugType(CheckNames[*CheckKind], "Bad free", "Memory Error"));
|
||||
|
||||
SmallString<100> buf;
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
|
||||
@ -1253,7 +1267,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
|
||||
printExpectedAllocName(os, C, DeallocExpr);
|
||||
}
|
||||
|
||||
BugReport *R = new BugReport(*BT_BadFree, os.str(), N);
|
||||
BugReport *R = new BugReport(*BT_BadFree[*CheckKind], os.str(), N);
|
||||
R->markInteresting(MR);
|
||||
R->addRange(Range);
|
||||
C.emitReport(R);
|
||||
@ -1267,14 +1281,15 @@ void MallocChecker::ReportMismatchedDealloc(CheckerContext &C,
|
||||
SymbolRef Sym,
|
||||
bool OwnershipTransferred) const {
|
||||
|
||||
if (!Filter.CMismatchedDeallocatorChecker)
|
||||
if (!ChecksEnabled[CK_MismatchedDeallocatorChecker])
|
||||
return;
|
||||
|
||||
if (ExplodedNode *N = C.generateSink()) {
|
||||
if (!BT_MismatchedDealloc)
|
||||
BT_MismatchedDealloc.reset(new BugType("Bad deallocator",
|
||||
"Memory Error"));
|
||||
|
||||
BT_MismatchedDealloc.reset(
|
||||
new BugType(CheckNames[CK_MismatchedDeallocatorChecker],
|
||||
"Bad deallocator", "Memory Error"));
|
||||
|
||||
SmallString<100> buf;
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
|
||||
@ -1318,19 +1333,23 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
|
||||
SourceRange Range, const Expr *DeallocExpr,
|
||||
const Expr *AllocExpr) const {
|
||||
|
||||
if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic &&
|
||||
!Filter.CNewDeleteChecker)
|
||||
if (!ChecksEnabled[CK_MallocOptimistic] &&
|
||||
!ChecksEnabled[CK_MallocPessimistic] &&
|
||||
!ChecksEnabled[CK_NewDeleteChecker])
|
||||
return;
|
||||
|
||||
if (!isTrackedByCurrentChecker(C, AllocExpr))
|
||||
Optional<MallocChecker::CheckKind> CheckKind =
|
||||
getCheckIfTracked(C, AllocExpr);
|
||||
if (!CheckKind.hasValue())
|
||||
return;
|
||||
|
||||
ExplodedNode *N = C.generateSink();
|
||||
if (N == NULL)
|
||||
return;
|
||||
|
||||
if (!BT_OffsetFree)
|
||||
BT_OffsetFree.reset(new BugType("Offset free", "Memory Error"));
|
||||
if (!BT_OffsetFree[*CheckKind])
|
||||
BT_OffsetFree[*CheckKind].reset(
|
||||
new BugType(CheckNames[*CheckKind], "Offset free", "Memory Error"));
|
||||
|
||||
SmallString<100> buf;
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
@ -1361,7 +1380,7 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
|
||||
else
|
||||
os << "allocated memory";
|
||||
|
||||
BugReport *R = new BugReport(*BT_OffsetFree, os.str(), N);
|
||||
BugReport *R = new BugReport(*BT_OffsetFree[*CheckKind], os.str(), N);
|
||||
R->markInteresting(MR->getBaseRegion());
|
||||
R->addRange(Range);
|
||||
C.emitReport(R);
|
||||
@ -1370,18 +1389,21 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
|
||||
void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range,
|
||||
SymbolRef Sym) const {
|
||||
|
||||
if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic &&
|
||||
!Filter.CNewDeleteChecker)
|
||||
if (!ChecksEnabled[CK_MallocOptimistic] &&
|
||||
!ChecksEnabled[CK_MallocPessimistic] &&
|
||||
!ChecksEnabled[CK_NewDeleteChecker])
|
||||
return;
|
||||
|
||||
if (!isTrackedByCurrentChecker(C, Sym))
|
||||
Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
|
||||
if (!CheckKind.hasValue())
|
||||
return;
|
||||
|
||||
if (ExplodedNode *N = C.generateSink()) {
|
||||
if (!BT_UseFree)
|
||||
BT_UseFree.reset(new BugType("Use-after-free", "Memory Error"));
|
||||
if (!BT_UseFree[*CheckKind])
|
||||
BT_UseFree[*CheckKind].reset(new BugType(
|
||||
CheckNames[*CheckKind], "Use-after-free", "Memory Error"));
|
||||
|
||||
BugReport *R = new BugReport(*BT_UseFree,
|
||||
BugReport *R = new BugReport(*BT_UseFree[*CheckKind],
|
||||
"Use of memory after it is freed", N);
|
||||
|
||||
R->markInteresting(Sym);
|
||||
@ -1395,21 +1417,25 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range,
|
||||
bool Released, SymbolRef Sym,
|
||||
SymbolRef PrevSym) const {
|
||||
|
||||
if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic &&
|
||||
!Filter.CNewDeleteChecker)
|
||||
if (!ChecksEnabled[CK_MallocOptimistic] &&
|
||||
!ChecksEnabled[CK_MallocPessimistic] &&
|
||||
!ChecksEnabled[CK_NewDeleteChecker])
|
||||
return;
|
||||
|
||||
if (!isTrackedByCurrentChecker(C, Sym))
|
||||
Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
|
||||
if (!CheckKind.hasValue())
|
||||
return;
|
||||
|
||||
if (ExplodedNode *N = C.generateSink()) {
|
||||
if (!BT_DoubleFree)
|
||||
BT_DoubleFree.reset(new BugType("Double free", "Memory Error"));
|
||||
if (!BT_DoubleFree[*CheckKind])
|
||||
BT_DoubleFree[*CheckKind].reset(
|
||||
new BugType(CheckNames[*CheckKind], "Double free", "Memory Error"));
|
||||
|
||||
BugReport *R = new BugReport(*BT_DoubleFree,
|
||||
(Released ? "Attempt to free released memory"
|
||||
: "Attempt to free non-owned memory"),
|
||||
N);
|
||||
BugReport *R =
|
||||
new BugReport(*BT_DoubleFree[*CheckKind],
|
||||
(Released ? "Attempt to free released memory"
|
||||
: "Attempt to free non-owned memory"),
|
||||
N);
|
||||
R->addRange(Range);
|
||||
R->markInteresting(Sym);
|
||||
if (PrevSym)
|
||||
@ -1421,15 +1447,18 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range,
|
||||
|
||||
void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const {
|
||||
|
||||
if (!Filter.CNewDeleteChecker)
|
||||
if (!ChecksEnabled[CK_NewDeleteChecker])
|
||||
return;
|
||||
|
||||
if (!isTrackedByCurrentChecker(C, Sym))
|
||||
Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
|
||||
if (!CheckKind.hasValue())
|
||||
return;
|
||||
assert(*CheckKind == CK_NewDeleteChecker && "invalid check kind");
|
||||
|
||||
if (ExplodedNode *N = C.generateSink()) {
|
||||
if (!BT_DoubleDelete)
|
||||
BT_DoubleDelete.reset(new BugType("Double delete", "Memory Error"));
|
||||
BT_DoubleDelete.reset(new BugType(CheckNames[CK_NewDeleteChecker],
|
||||
"Double delete", "Memory Error"));
|
||||
|
||||
BugReport *R = new BugReport(*BT_DoubleDelete,
|
||||
"Attempt to delete released memory", N);
|
||||
@ -1601,31 +1630,34 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
|
||||
void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
|
||||
CheckerContext &C) const {
|
||||
|
||||
if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic &&
|
||||
!Filter.CNewDeleteLeaksChecker)
|
||||
if (!ChecksEnabled[CK_MallocOptimistic] &&
|
||||
!ChecksEnabled[CK_MallocPessimistic] &&
|
||||
!ChecksEnabled[CK_NewDeleteLeaksChecker])
|
||||
return;
|
||||
|
||||
const RefState *RS = C.getState()->get<RegionState>(Sym);
|
||||
assert(RS && "cannot leak an untracked symbol");
|
||||
AllocationFamily Family = RS->getAllocationFamily();
|
||||
if (!isTrackedByCurrentChecker(Family))
|
||||
Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
|
||||
if (!CheckKind.hasValue())
|
||||
return;
|
||||
|
||||
// Special case for new and new[]; these are controlled by a separate checker
|
||||
// flag so that they can be selectively disabled.
|
||||
if (Family == AF_CXXNew || Family == AF_CXXNewArray)
|
||||
if (!Filter.CNewDeleteLeaksChecker)
|
||||
if (!ChecksEnabled[CK_NewDeleteLeaksChecker])
|
||||
return;
|
||||
|
||||
assert(N);
|
||||
if (!BT_Leak) {
|
||||
BT_Leak.reset(new BugType("Memory leak", "Memory Error"));
|
||||
if (!BT_Leak[*CheckKind]) {
|
||||
BT_Leak[*CheckKind].reset(
|
||||
new BugType(CheckNames[*CheckKind], "Memory leak", "Memory Error"));
|
||||
// Leaks should not be reported if they are post-dominated by a sink:
|
||||
// (1) Sinks are higher importance bugs.
|
||||
// (2) NoReturnFunctionChecker uses sink nodes to represent paths ending
|
||||
// with __noreturn functions such as assert() or exit(). We choose not
|
||||
// to report leaks on such paths.
|
||||
BT_Leak->setSuppressOnSink(true);
|
||||
BT_Leak[*CheckKind]->setSuppressOnSink(true);
|
||||
}
|
||||
|
||||
// Most bug reports are cached at the location where they occurred.
|
||||
@ -1656,9 +1688,9 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
|
||||
os << "Potential memory leak";
|
||||
}
|
||||
|
||||
BugReport *R = new BugReport(*BT_Leak, os.str(), N,
|
||||
LocUsedForUniqueing,
|
||||
AllocNode->getLocationContext()->getDecl());
|
||||
BugReport *R =
|
||||
new BugReport(*BT_Leak[*CheckKind], os.str(), N, LocUsedForUniqueing,
|
||||
AllocNode->getLocationContext()->getDecl());
|
||||
R->markInteresting(Sym);
|
||||
R->addVisitor(new MallocBugVisitor(Sym, true));
|
||||
C.emitReport(R);
|
||||
@ -1732,11 +1764,12 @@ void MallocChecker::checkPreCall(const CallEvent &Call,
|
||||
if (!FD)
|
||||
return;
|
||||
|
||||
if ((Filter.CMallocOptimistic || Filter.CMallocPessimistic) &&
|
||||
if ((ChecksEnabled[CK_MallocOptimistic] ||
|
||||
ChecksEnabled[CK_MallocPessimistic]) &&
|
||||
isFreeFunction(FD, C.getASTContext()))
|
||||
return;
|
||||
|
||||
if (Filter.CNewDeleteChecker &&
|
||||
if (ChecksEnabled[CK_NewDeleteChecker] &&
|
||||
isStandardNewDelete(FD, C.getASTContext()))
|
||||
return;
|
||||
}
|
||||
@ -2230,17 +2263,26 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,
|
||||
|
||||
void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {
|
||||
registerCStringCheckerBasic(mgr);
|
||||
mgr.registerChecker<MallocChecker>()->Filter.CNewDeleteLeaksChecker = true;
|
||||
MallocChecker *checker = mgr.registerChecker<MallocChecker>();
|
||||
checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true;
|
||||
checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] =
|
||||
mgr.getCurrentCheckName();
|
||||
// We currently treat NewDeleteLeaks checker as a subchecker of NewDelete
|
||||
// checker.
|
||||
mgr.registerChecker<MallocChecker>()->Filter.CNewDeleteChecker = true;
|
||||
if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker]) {
|
||||
checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker] = true;
|
||||
checker->CheckNames[MallocChecker::CK_NewDeleteChecker] =
|
||||
mgr.getCurrentCheckName();
|
||||
}
|
||||
}
|
||||
|
||||
#define REGISTER_CHECKER(name) \
|
||||
void ento::register##name(CheckerManager &mgr) {\
|
||||
registerCStringCheckerBasic(mgr); \
|
||||
mgr.registerChecker<MallocChecker>()->Filter.C##name = true;\
|
||||
}
|
||||
#define REGISTER_CHECKER(name) \
|
||||
void ento::register##name(CheckerManager &mgr) { \
|
||||
registerCStringCheckerBasic(mgr); \
|
||||
MallocChecker *checker = mgr.registerChecker<MallocChecker>(); \
|
||||
checker->ChecksEnabled[MallocChecker::CK_##name] = true; \
|
||||
checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \
|
||||
}
|
||||
|
||||
REGISTER_CHECKER(MallocPessimistic)
|
||||
REGISTER_CHECKER(MallocOptimistic)
|
||||
|
@ -213,11 +213,12 @@ void MallocOverflowSecurityChecker::OutputPossibleOverflows(
|
||||
e = PossibleMallocOverflows.end();
|
||||
i != e;
|
||||
++i) {
|
||||
BR.EmitBasicReport(D, "malloc() size overflow", categories::UnixAPI,
|
||||
"the computation of the size of the memory allocation may overflow",
|
||||
PathDiagnosticLocation::createOperatorLoc(i->mulop,
|
||||
BR.getSourceManager()),
|
||||
i->mulop->getSourceRange());
|
||||
BR.EmitBasicReport(
|
||||
D, this, "malloc() size overflow", categories::UnixAPI,
|
||||
"the computation of the size of the memory allocation may overflow",
|
||||
PathDiagnosticLocation::createOperatorLoc(i->mulop,
|
||||
BR.getSourceManager()),
|
||||
i->mulop->getSourceRange());
|
||||
}
|
||||
}
|
||||
|
||||
@ -262,6 +263,7 @@ void MallocOverflowSecurityChecker::checkASTCodeBody(const Decl *D,
|
||||
OutputPossibleOverflows(PossibleMallocOverflows, D, BR, mgr);
|
||||
}
|
||||
|
||||
void ento::registerMallocOverflowSecurityChecker(CheckerManager &mgr) {
|
||||
void
|
||||
ento::registerMallocOverflowSecurityChecker(CheckerManager &mgr) {
|
||||
mgr.registerChecker<MallocOverflowSecurityChecker>();
|
||||
}
|
||||
|
@ -236,10 +236,8 @@ public:
|
||||
PathDiagnosticLocation::createBegin(i->AllocCall->getCallee(),
|
||||
BR.getSourceManager(), ADC);
|
||||
|
||||
BR.EmitBasicReport(D, "Allocator sizeof operand mismatch",
|
||||
categories::UnixAPI,
|
||||
OS.str(),
|
||||
L, Ranges);
|
||||
BR.EmitBasicReport(D, this, "Allocator sizeof operand mismatch",
|
||||
categories::UnixAPI, OS.str(), L, Ranges);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
||||
return;
|
||||
|
||||
if (!BT)
|
||||
BT.reset(new BugType("Use -drain instead of -release",
|
||||
BT.reset(new BugType(this, "Use -drain instead of -release",
|
||||
"API Upgrade (Apple)"));
|
||||
|
||||
ExplodedNode *N = C.addTransition();
|
||||
|
@ -75,7 +75,7 @@ void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D,
|
||||
"error occurred";
|
||||
PathDiagnosticLocation L =
|
||||
PathDiagnosticLocation::create(D, BR.getSourceManager());
|
||||
BR.EmitBasicReport(D, "Bad return type when passing NSError**",
|
||||
BR.EmitBasicReport(D, this, "Bad return type when passing NSError**",
|
||||
"Coding conventions (Apple)", err, L);
|
||||
}
|
||||
}
|
||||
@ -123,7 +123,7 @@ void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
|
||||
"error occurred";
|
||||
PathDiagnosticLocation L =
|
||||
PathDiagnosticLocation::create(D, BR.getSourceManager());
|
||||
BR.EmitBasicReport(D, "Bad return type when passing CFErrorRef*",
|
||||
BR.EmitBasicReport(D, this, "Bad return type when passing CFErrorRef*",
|
||||
"Coding conventions (Apple)", err, L);
|
||||
}
|
||||
}
|
||||
@ -136,14 +136,16 @@ namespace {
|
||||
|
||||
class NSErrorDerefBug : public BugType {
|
||||
public:
|
||||
NSErrorDerefBug() : BugType("NSError** null dereference",
|
||||
"Coding conventions (Apple)") {}
|
||||
NSErrorDerefBug(const CheckerBase *Checker)
|
||||
: BugType(Checker, "NSError** null dereference",
|
||||
"Coding conventions (Apple)") {}
|
||||
};
|
||||
|
||||
class CFErrorDerefBug : public BugType {
|
||||
public:
|
||||
CFErrorDerefBug() : BugType("CFErrorRef* null dereference",
|
||||
"Coding conventions (Apple)") {}
|
||||
CFErrorDerefBug(const CheckerBase *Checker)
|
||||
: BugType(Checker, "CFErrorRef* null dereference",
|
||||
"Coding conventions (Apple)") {}
|
||||
};
|
||||
|
||||
}
|
||||
@ -264,11 +266,10 @@ void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const {
|
||||
|
||||
BugType *bug = 0;
|
||||
if (isNSError)
|
||||
bug = new NSErrorDerefBug();
|
||||
bug = new NSErrorDerefBug(this);
|
||||
else
|
||||
bug = new CFErrorDerefBug();
|
||||
BugReport *report = new BugReport(*bug, os.str(),
|
||||
event.SinkNode);
|
||||
bug = new CFErrorDerefBug(this);
|
||||
BugReport *report = new BugReport(*bug, os.str(), event.SinkNode);
|
||||
BR.emitReport(report);
|
||||
}
|
||||
|
||||
@ -305,14 +306,14 @@ static bool IsCFError(QualType T, IdentifierInfo *II) {
|
||||
|
||||
void ento::registerNSErrorChecker(CheckerManager &mgr) {
|
||||
mgr.registerChecker<NSErrorMethodChecker>();
|
||||
NSOrCFErrorDerefChecker *
|
||||
checker = mgr.registerChecker<NSOrCFErrorDerefChecker>();
|
||||
NSOrCFErrorDerefChecker *checker =
|
||||
mgr.registerChecker<NSOrCFErrorDerefChecker>();
|
||||
checker->ShouldCheckNSError = true;
|
||||
}
|
||||
|
||||
void ento::registerCFErrorChecker(CheckerManager &mgr) {
|
||||
mgr.registerChecker<CFErrorFunctionChecker>();
|
||||
NSOrCFErrorDerefChecker *
|
||||
checker = mgr.registerChecker<NSOrCFErrorDerefChecker>();
|
||||
NSOrCFErrorDerefChecker *checker =
|
||||
mgr.registerChecker<NSOrCFErrorDerefChecker>();
|
||||
checker->ShouldCheckCFError = true;
|
||||
}
|
||||
|
@ -150,7 +150,6 @@ void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg,
|
||||
C.generateSink();
|
||||
}
|
||||
|
||||
|
||||
void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) {
|
||||
mgr.registerChecker<NoReturnFunctionChecker>();
|
||||
}
|
||||
|
@ -162,8 +162,7 @@ BugReport *NonNullParamChecker::genReportNullAttrNonNull(
|
||||
// the BugReport is passed to 'EmitWarning'.
|
||||
if (!BTAttrNonNull)
|
||||
BTAttrNonNull.reset(new BugType(
|
||||
"Argument with 'nonnull' attribute passed null",
|
||||
"API"));
|
||||
this, "Argument with 'nonnull' attribute passed null", "API"));
|
||||
|
||||
BugReport *R = new BugReport(*BTAttrNonNull,
|
||||
"Null pointer passed as an argument to a 'nonnull' parameter",
|
||||
@ -177,7 +176,7 @@ BugReport *NonNullParamChecker::genReportNullAttrNonNull(
|
||||
BugReport *NonNullParamChecker::genReportReferenceToNullPointer(
|
||||
const ExplodedNode *ErrorNode, const Expr *ArgE) const {
|
||||
if (!BTNullRefArg)
|
||||
BTNullRefArg.reset(new BuiltinBug("Dereference of null pointer"));
|
||||
BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer"));
|
||||
|
||||
BugReport *R = new BugReport(*BTNullRefArg,
|
||||
"Forming reference to null pointer",
|
||||
|
@ -45,8 +45,8 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S,
|
||||
if (V.getAs<UndefinedVal>()) {
|
||||
if (ExplodedNode *N = C.generateSink()) {
|
||||
if (!BT_undef)
|
||||
BT_undef.reset(new BuiltinBug("Uninitialized value used as mutex "
|
||||
"for @synchronized"));
|
||||
BT_undef.reset(new BuiltinBug(this, "Uninitialized value used as mutex "
|
||||
"for @synchronized"));
|
||||
BugReport *report =
|
||||
new BugReport(*BT_undef, BT_undef->getDescription(), N);
|
||||
bugreporter::trackNullOrUndefValue(N, Ex, *report);
|
||||
@ -68,8 +68,9 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S,
|
||||
// a null mutex just means no synchronization occurs.
|
||||
if (ExplodedNode *N = C.addTransition(nullState)) {
|
||||
if (!BT_null)
|
||||
BT_null.reset(new BuiltinBug("Nil value used as mutex for @synchronized() "
|
||||
"(no synchronization will occur)"));
|
||||
BT_null.reset(new BuiltinBug(
|
||||
this, "Nil value used as mutex for @synchronized() "
|
||||
"(no synchronization will occur)"));
|
||||
BugReport *report =
|
||||
new BugReport(*BT_null, BT_null->getDescription(), N);
|
||||
bugreporter::trackNullOrUndefValue(N, Ex, *report);
|
||||
|
@ -27,6 +27,7 @@ using namespace ento;
|
||||
namespace {
|
||||
class WalkAST : public StmtVisitor<WalkAST> {
|
||||
BugReporter &BR;
|
||||
const CheckerBase *Checker;
|
||||
AnalysisDeclContext* AC;
|
||||
ASTContext &ASTC;
|
||||
uint64_t PtrWidth;
|
||||
@ -71,9 +72,9 @@ class WalkAST : public StmtVisitor<WalkAST> {
|
||||
}
|
||||
|
||||
public:
|
||||
WalkAST(BugReporter &br, AnalysisDeclContext* ac)
|
||||
: BR(br), AC(ac), ASTC(AC->getASTContext()),
|
||||
PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {}
|
||||
WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)
|
||||
: BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()),
|
||||
PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {}
|
||||
|
||||
// Statement visitor methods.
|
||||
void VisitChildren(Stmt *S);
|
||||
@ -142,9 +143,9 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {
|
||||
|
||||
PathDiagnosticLocation CELoc =
|
||||
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
|
||||
BR.EmitBasicReport(AC->getDecl(),
|
||||
OsName.str(), categories::CoreFoundationObjectiveC,
|
||||
Os.str(), CELoc, Arg->getSourceRange());
|
||||
BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(),
|
||||
categories::CoreFoundationObjectiveC, Os.str(), CELoc,
|
||||
Arg->getSourceRange());
|
||||
}
|
||||
|
||||
// Recurse and check children.
|
||||
@ -163,7 +164,7 @@ public:
|
||||
|
||||
void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
|
||||
BugReporter &BR) const {
|
||||
WalkAST walker(BR, Mgr.getAnalysisDeclContext(D));
|
||||
WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D));
|
||||
walker.Visit(D->getBody());
|
||||
}
|
||||
};
|
||||
|
@ -33,7 +33,7 @@ class ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>,
|
||||
mutable OwningPtr<BugType> BT;
|
||||
inline void initBugType() const {
|
||||
if (!BT)
|
||||
BT.reset(new BugType("CFArray API",
|
||||
BT.reset(new BugType(this, "CFArray API",
|
||||
categories::CoreFoundationObjectiveC));
|
||||
}
|
||||
|
||||
|
@ -212,7 +212,7 @@ void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
|
||||
<< "' instance method in " << SuperclassName.str() << " subclass '"
|
||||
<< *D << "' is missing a [super " << S.getAsString() << "] call";
|
||||
|
||||
BR.EmitBasicReport(MD, Name, categories::CoreFoundationObjectiveC,
|
||||
BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC,
|
||||
os.str(), DLoc);
|
||||
}
|
||||
}
|
||||
|
@ -81,10 +81,10 @@ public:
|
||||
namespace {
|
||||
|
||||
class InitSelfBug : public BugType {
|
||||
const std::string desc;
|
||||
public:
|
||||
InitSelfBug() : BugType("Missing \"self = [(super or self) init...]\"",
|
||||
categories::CoreFoundationObjectiveC) {}
|
||||
InitSelfBug(const CheckerBase *Checker)
|
||||
: BugType(Checker, "Missing \"self = [(super or self) init...]\"",
|
||||
categories::CoreFoundationObjectiveC) {}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
@ -147,7 +147,8 @@ static bool isInvalidSelf(const Expr *E, CheckerContext &C) {
|
||||
}
|
||||
|
||||
static void checkForInvalidSelf(const Expr *E, CheckerContext &C,
|
||||
const char *errorStr) {
|
||||
const char *errorStr,
|
||||
const CheckerBase *Checker) {
|
||||
if (!E)
|
||||
return;
|
||||
|
||||
@ -162,8 +163,7 @@ static void checkForInvalidSelf(const Expr *E, CheckerContext &C,
|
||||
if (!N)
|
||||
return;
|
||||
|
||||
BugReport *report =
|
||||
new BugReport(*new InitSelfBug(), errorStr, N);
|
||||
BugReport *report = new BugReport(*new InitSelfBug(Checker), errorStr, N);
|
||||
C.emitReport(report);
|
||||
}
|
||||
|
||||
@ -205,9 +205,11 @@ void ObjCSelfInitChecker::checkPostStmt(const ObjCIvarRefExpr *E,
|
||||
C.getCurrentAnalysisDeclContext()->getDecl())))
|
||||
return;
|
||||
|
||||
checkForInvalidSelf(E->getBase(), C,
|
||||
"Instance variable used while 'self' is not set to the result of "
|
||||
"'[(super or self) init...]'");
|
||||
checkForInvalidSelf(
|
||||
E->getBase(), C,
|
||||
"Instance variable used while 'self' is not set to the result of "
|
||||
"'[(super or self) init...]'",
|
||||
this);
|
||||
}
|
||||
|
||||
void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S,
|
||||
@ -218,8 +220,9 @@ void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S,
|
||||
return;
|
||||
|
||||
checkForInvalidSelf(S->getRetValue(), C,
|
||||
"Returning 'self' while it is not set to the result of "
|
||||
"'[(super or self) init...]'");
|
||||
"Returning 'self' while it is not set to the result of "
|
||||
"'[(super or self) init...]'",
|
||||
this);
|
||||
}
|
||||
|
||||
// When a call receives a reference to 'self', [Pre/Post]Call pass
|
||||
|
@ -111,7 +111,8 @@ static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID,
|
||||
}
|
||||
|
||||
static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
|
||||
BugReporter &BR) {
|
||||
BugReporter &BR,
|
||||
const CheckerBase *Checker) {
|
||||
|
||||
const ObjCInterfaceDecl *ID = D->getClassInterface();
|
||||
IvarUsageMap M;
|
||||
@ -172,7 +173,7 @@ static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
|
||||
|
||||
PathDiagnosticLocation L =
|
||||
PathDiagnosticLocation::create(I->first, BR.getSourceManager());
|
||||
BR.EmitBasicReport(D, "Unused instance variable", "Optimization",
|
||||
BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization",
|
||||
os.str(), L);
|
||||
}
|
||||
}
|
||||
@ -187,7 +188,7 @@ class ObjCUnusedIvarsChecker : public Checker<
|
||||
public:
|
||||
void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
|
||||
BugReporter &BR) const {
|
||||
checkObjCUnusedIvar(D, BR);
|
||||
checkObjCUnusedIvar(D, BR, this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -53,10 +53,11 @@ void PointerArithChecker::checkPreStmt(const BinaryOperator *B,
|
||||
|
||||
if (ExplodedNode *N = C.addTransition()) {
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug("Dangerous pointer arithmetic",
|
||||
"Pointer arithmetic done on non-array variables "
|
||||
"means reliance on memory layout, which is "
|
||||
"dangerous."));
|
||||
BT.reset(
|
||||
new BuiltinBug(this, "Dangerous pointer arithmetic",
|
||||
"Pointer arithmetic done on non-array variables "
|
||||
"means reliance on memory layout, which is "
|
||||
"dangerous."));
|
||||
BugReport *R = new BugReport(*BT, BT->getDescription(), N);
|
||||
R->addRange(B->getSourceRange());
|
||||
C.emitReport(R);
|
||||
|
@ -62,9 +62,10 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
|
||||
|
||||
if (ExplodedNode *N = C.addTransition()) {
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug("Pointer subtraction",
|
||||
"Subtraction of two pointers that do not point to "
|
||||
"the same memory chunk may cause incorrect result."));
|
||||
BT.reset(
|
||||
new BuiltinBug(this, "Pointer subtraction",
|
||||
"Subtraction of two pointers that do not point to "
|
||||
"the same memory chunk may cause incorrect result."));
|
||||
BugReport *R = new BugReport(*BT, BT->getDescription(), N);
|
||||
R->addRange(B->getSourceRange());
|
||||
C.emitReport(R);
|
||||
|
@ -102,7 +102,7 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
|
||||
|
||||
if (state->contains<LockSet>(lockR)) {
|
||||
if (!BT_doublelock)
|
||||
BT_doublelock.reset(new BugType("Double locking", "Lock checker"));
|
||||
BT_doublelock.reset(new BugType(this, "Double locking", "Lock checker"));
|
||||
ExplodedNode *N = C.generateSink();
|
||||
if (!N)
|
||||
return;
|
||||
@ -165,7 +165,7 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
|
||||
const MemRegion *firstLockR = LS.getHead();
|
||||
if (firstLockR != lockR) {
|
||||
if (!BT_lor)
|
||||
BT_lor.reset(new BugType("Lock order reversal", "Lock checker"));
|
||||
BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker"));
|
||||
ExplodedNode *N = C.generateSink();
|
||||
if (!N)
|
||||
return;
|
||||
@ -184,7 +184,6 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
|
||||
C.addTransition(state);
|
||||
}
|
||||
|
||||
|
||||
void ento::registerPthreadLockChecker(CheckerManager &mgr) {
|
||||
mgr.registerChecker<PthreadLockChecker>();
|
||||
}
|
||||
|
@ -1547,8 +1547,9 @@ namespace {
|
||||
|
||||
class CFRefBug : public BugType {
|
||||
protected:
|
||||
CFRefBug(StringRef name)
|
||||
: BugType(name, categories::MemoryCoreFoundationObjectiveC) {}
|
||||
CFRefBug(const CheckerBase *checker, StringRef name)
|
||||
: BugType(checker, name, categories::MemoryCoreFoundationObjectiveC) {}
|
||||
|
||||
public:
|
||||
|
||||
// FIXME: Eventually remove.
|
||||
@ -1559,7 +1560,8 @@ namespace {
|
||||
|
||||
class UseAfterRelease : public CFRefBug {
|
||||
public:
|
||||
UseAfterRelease() : CFRefBug("Use-after-release") {}
|
||||
UseAfterRelease(const CheckerBase *checker)
|
||||
: CFRefBug(checker, "Use-after-release") {}
|
||||
|
||||
const char *getDescription() const {
|
||||
return "Reference-counted object is used after it is released";
|
||||
@ -1568,7 +1570,7 @@ namespace {
|
||||
|
||||
class BadRelease : public CFRefBug {
|
||||
public:
|
||||
BadRelease() : CFRefBug("Bad release") {}
|
||||
BadRelease(const CheckerBase *checker) : CFRefBug(checker, "Bad release") {}
|
||||
|
||||
const char *getDescription() const {
|
||||
return "Incorrect decrement of the reference count of an object that is "
|
||||
@ -1578,8 +1580,8 @@ namespace {
|
||||
|
||||
class DeallocGC : public CFRefBug {
|
||||
public:
|
||||
DeallocGC()
|
||||
: CFRefBug("-dealloc called while using garbage collection") {}
|
||||
DeallocGC(const CheckerBase *checker)
|
||||
: CFRefBug(checker, "-dealloc called while using garbage collection") {}
|
||||
|
||||
const char *getDescription() const {
|
||||
return "-dealloc called while using garbage collection";
|
||||
@ -1588,8 +1590,8 @@ namespace {
|
||||
|
||||
class DeallocNotOwned : public CFRefBug {
|
||||
public:
|
||||
DeallocNotOwned()
|
||||
: CFRefBug("-dealloc sent to non-exclusively owned object") {}
|
||||
DeallocNotOwned(const CheckerBase *checker)
|
||||
: CFRefBug(checker, "-dealloc sent to non-exclusively owned object") {}
|
||||
|
||||
const char *getDescription() const {
|
||||
return "-dealloc sent to object that may be referenced elsewhere";
|
||||
@ -1598,8 +1600,8 @@ namespace {
|
||||
|
||||
class OverAutorelease : public CFRefBug {
|
||||
public:
|
||||
OverAutorelease()
|
||||
: CFRefBug("Object autoreleased too many times") {}
|
||||
OverAutorelease(const CheckerBase *checker)
|
||||
: CFRefBug(checker, "Object autoreleased too many times") {}
|
||||
|
||||
const char *getDescription() const {
|
||||
return "Object autoreleased too many times";
|
||||
@ -1608,8 +1610,8 @@ namespace {
|
||||
|
||||
class ReturnedNotOwnedForOwned : public CFRefBug {
|
||||
public:
|
||||
ReturnedNotOwnedForOwned()
|
||||
: CFRefBug("Method should return an owned object") {}
|
||||
ReturnedNotOwnedForOwned(const CheckerBase *checker)
|
||||
: CFRefBug(checker, "Method should return an owned object") {}
|
||||
|
||||
const char *getDescription() const {
|
||||
return "Object with a +0 retain count returned to caller where a +1 "
|
||||
@ -1619,8 +1621,7 @@ namespace {
|
||||
|
||||
class Leak : public CFRefBug {
|
||||
public:
|
||||
Leak(StringRef name)
|
||||
: CFRefBug(name) {
|
||||
Leak(const CheckerBase *checker, StringRef name) : CFRefBug(checker, name) {
|
||||
// Leaks should not be reported if they are post-dominated by a sink.
|
||||
setSuppressOnSink(true);
|
||||
}
|
||||
@ -2406,17 +2407,18 @@ public:
|
||||
bool GCEnabled) const {
|
||||
if (GCEnabled) {
|
||||
if (!leakWithinFunctionGC)
|
||||
leakWithinFunctionGC.reset(new Leak("Leak of object when using "
|
||||
"garbage collection"));
|
||||
leakWithinFunctionGC.reset(new Leak(this, "Leak of object when using "
|
||||
"garbage collection"));
|
||||
return leakWithinFunctionGC.get();
|
||||
} else {
|
||||
if (!leakWithinFunction) {
|
||||
if (LOpts.getGC() == LangOptions::HybridGC) {
|
||||
leakWithinFunction.reset(new Leak("Leak of object when not using "
|
||||
leakWithinFunction.reset(new Leak(this,
|
||||
"Leak of object when not using "
|
||||
"garbage collection (GC) in "
|
||||
"dual GC/non-GC code"));
|
||||
} else {
|
||||
leakWithinFunction.reset(new Leak("Leak"));
|
||||
leakWithinFunction.reset(new Leak(this, "Leak"));
|
||||
}
|
||||
}
|
||||
return leakWithinFunction.get();
|
||||
@ -2426,17 +2428,19 @@ public:
|
||||
CFRefBug *getLeakAtReturnBug(const LangOptions &LOpts, bool GCEnabled) const {
|
||||
if (GCEnabled) {
|
||||
if (!leakAtReturnGC)
|
||||
leakAtReturnGC.reset(new Leak("Leak of returned object when using "
|
||||
leakAtReturnGC.reset(new Leak(this,
|
||||
"Leak of returned object when using "
|
||||
"garbage collection"));
|
||||
return leakAtReturnGC.get();
|
||||
} else {
|
||||
if (!leakAtReturn) {
|
||||
if (LOpts.getGC() == LangOptions::HybridGC) {
|
||||
leakAtReturn.reset(new Leak("Leak of returned object when not using "
|
||||
leakAtReturn.reset(new Leak(this,
|
||||
"Leak of returned object when not using "
|
||||
"garbage collection (GC) in dual "
|
||||
"GC/non-GC code"));
|
||||
} else {
|
||||
leakAtReturn.reset(new Leak("Leak of returned object"));
|
||||
leakAtReturn.reset(new Leak(this, "Leak of returned object"));
|
||||
}
|
||||
}
|
||||
return leakAtReturn.get();
|
||||
@ -3075,22 +3079,22 @@ void RetainCountChecker::processNonLeakError(ProgramStateRef St,
|
||||
llvm_unreachable("Unhandled error.");
|
||||
case RefVal::ErrorUseAfterRelease:
|
||||
if (!useAfterRelease)
|
||||
useAfterRelease.reset(new UseAfterRelease());
|
||||
useAfterRelease.reset(new UseAfterRelease(this));
|
||||
BT = &*useAfterRelease;
|
||||
break;
|
||||
case RefVal::ErrorReleaseNotOwned:
|
||||
if (!releaseNotOwned)
|
||||
releaseNotOwned.reset(new BadRelease());
|
||||
releaseNotOwned.reset(new BadRelease(this));
|
||||
BT = &*releaseNotOwned;
|
||||
break;
|
||||
case RefVal::ErrorDeallocGC:
|
||||
if (!deallocGC)
|
||||
deallocGC.reset(new DeallocGC());
|
||||
deallocGC.reset(new DeallocGC(this));
|
||||
BT = &*deallocGC;
|
||||
break;
|
||||
case RefVal::ErrorDeallocNotOwned:
|
||||
if (!deallocNotOwned)
|
||||
deallocNotOwned.reset(new DeallocNotOwned());
|
||||
deallocNotOwned.reset(new DeallocNotOwned(this));
|
||||
BT = &*deallocNotOwned;
|
||||
break;
|
||||
}
|
||||
@ -3342,7 +3346,7 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
|
||||
ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag);
|
||||
if (N) {
|
||||
if (!returnNotOwnedForOwned)
|
||||
returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned());
|
||||
returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned(this));
|
||||
|
||||
CFRefReport *report =
|
||||
new CFRefReport(*returnNotOwnedForOwned,
|
||||
@ -3530,7 +3534,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
|
||||
os << "has a +" << V.getCount() << " retain count";
|
||||
|
||||
if (!overAutorelease)
|
||||
overAutorelease.reset(new OverAutorelease());
|
||||
overAutorelease.reset(new OverAutorelease(this));
|
||||
|
||||
const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
|
||||
CFRefReport *report =
|
||||
|
@ -69,9 +69,10 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
|
||||
// FIXME: This bug correspond to CWE-466. Eventually we should have bug
|
||||
// types explicitly reference such exploit categories (when applicable).
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug("Return of pointer value outside of expected range",
|
||||
"Returned pointer value points outside the original object "
|
||||
"(potential buffer overflow)"));
|
||||
BT.reset(new BuiltinBug(
|
||||
this, "Return of pointer value outside of expected range",
|
||||
"Returned pointer value points outside the original object "
|
||||
"(potential buffer overflow)"));
|
||||
|
||||
// FIXME: It would be nice to eventually make this diagnostic more clear,
|
||||
// e.g., by referencing the original declaration or by saying *why* this
|
||||
|
@ -94,9 +94,9 @@ static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE,
|
||||
|
||||
void ReturnUndefChecker::emitUndef(CheckerContext &C, const Expr *RetE) const {
|
||||
if (!BT_Undef)
|
||||
BT_Undef.reset(new BuiltinBug("Garbage return value",
|
||||
"Undefined or garbage value "
|
||||
"returned to caller"));
|
||||
BT_Undef.reset(
|
||||
new BuiltinBug(this, "Garbage return value",
|
||||
"Undefined or garbage value returned to caller"));
|
||||
emitBug(C, *BT_Undef, RetE);
|
||||
}
|
||||
|
||||
@ -113,7 +113,7 @@ void ReturnUndefChecker::checkReference(CheckerContext &C, const Expr *RetE,
|
||||
|
||||
// The return value is known to be null. Emit a bug report.
|
||||
if (!BT_NullReference)
|
||||
BT_NullReference.reset(new BuiltinBug("Returning null reference"));
|
||||
BT_NullReference.reset(new BuiltinBug(this, "Returning null reference"));
|
||||
|
||||
emitBug(C, *BT_NullReference, RetE, bugreporter::getDerefExpr(RetE));
|
||||
}
|
||||
|
@ -109,11 +109,11 @@ public:
|
||||
|
||||
SimpleStreamChecker::SimpleStreamChecker() : IIfopen(0), IIfclose(0) {
|
||||
// Initialize the bug types.
|
||||
DoubleCloseBugType.reset(new BugType("Double fclose",
|
||||
"Unix Stream API Error"));
|
||||
DoubleCloseBugType.reset(
|
||||
new BugType(this, "Double fclose", "Unix Stream API Error"));
|
||||
|
||||
LeakBugType.reset(new BugType("Resource Leak",
|
||||
"Unix Stream API Error"));
|
||||
LeakBugType.reset(
|
||||
new BugType(this, "Resource Leak", "Unix Stream API Error"));
|
||||
// Sinks are higher importance bugs as well as calls to assert() or exit(0).
|
||||
LeakBugType->setSuppressOnSink(true);
|
||||
}
|
||||
|
@ -100,8 +100,8 @@ void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *
|
||||
return;
|
||||
|
||||
if (!BT_returnstack)
|
||||
BT_returnstack.reset(
|
||||
new BuiltinBug("Return of address to stack-allocated memory"));
|
||||
BT_returnstack.reset(
|
||||
new BuiltinBug(this, "Return of address to stack-allocated memory"));
|
||||
|
||||
// Generate a report for this bug.
|
||||
SmallString<512> buf;
|
||||
@ -217,11 +217,11 @@ void StackAddrEscapeChecker::checkEndFunction(CheckerContext &Ctx) const {
|
||||
|
||||
if (!BT_stackleak)
|
||||
BT_stackleak.reset(
|
||||
new BuiltinBug("Stack address stored into global variable",
|
||||
"Stack address was saved into a global variable. "
|
||||
"This is dangerous because the address will become "
|
||||
"invalid after returning from the function"));
|
||||
|
||||
new BuiltinBug(this, "Stack address stored into global variable",
|
||||
"Stack address was saved into a global variable. "
|
||||
"This is dangerous because the address will become "
|
||||
"invalid after returning from the function"));
|
||||
|
||||
for (unsigned i = 0, e = cb.V.size(); i != e; ++i) {
|
||||
// Generate a report for this bug.
|
||||
SmallString<512> buf;
|
||||
|
@ -270,9 +270,10 @@ void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const {
|
||||
|
||||
if (ExplodedNode *N = C.addTransition(state)) {
|
||||
if (!BT_illegalwhence)
|
||||
BT_illegalwhence.reset(new BuiltinBug("Illegal whence argument",
|
||||
"The whence argument to fseek() should be "
|
||||
"SEEK_SET, SEEK_END, or SEEK_CUR."));
|
||||
BT_illegalwhence.reset(
|
||||
new BuiltinBug(this, "Illegal whence argument",
|
||||
"The whence argument to fseek() should be "
|
||||
"SEEK_SET, SEEK_END, or SEEK_CUR."));
|
||||
BugReport *R = new BugReport(*BT_illegalwhence,
|
||||
BT_illegalwhence->getDescription(), N);
|
||||
C.emitReport(R);
|
||||
@ -348,8 +349,8 @@ ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state,
|
||||
if (!stateNotNull && stateNull) {
|
||||
if (ExplodedNode *N = C.generateSink(stateNull)) {
|
||||
if (!BT_nullfp)
|
||||
BT_nullfp.reset(new BuiltinBug("NULL stream pointer",
|
||||
"Stream pointer might be NULL."));
|
||||
BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer",
|
||||
"Stream pointer might be NULL."));
|
||||
BugReport *R =new BugReport(*BT_nullfp, BT_nullfp->getDescription(), N);
|
||||
C.emitReport(R);
|
||||
}
|
||||
@ -378,9 +379,9 @@ ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE,
|
||||
ExplodedNode *N = C.generateSink();
|
||||
if (N) {
|
||||
if (!BT_doubleclose)
|
||||
BT_doubleclose.reset(new BuiltinBug("Double fclose",
|
||||
"Try to close a file Descriptor already"
|
||||
" closed. Cause undefined behaviour."));
|
||||
BT_doubleclose.reset(new BuiltinBug(
|
||||
this, "Double fclose", "Try to close a file Descriptor already"
|
||||
" closed. Cause undefined behaviour."));
|
||||
BugReport *R = new BugReport(*BT_doubleclose,
|
||||
BT_doubleclose->getDescription(), N);
|
||||
C.emitReport(R);
|
||||
@ -407,8 +408,9 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
|
||||
ExplodedNode *N = C.generateSink();
|
||||
if (N) {
|
||||
if (!BT_ResourceLeak)
|
||||
BT_ResourceLeak.reset(new BuiltinBug("Resource Leak",
|
||||
"Opened File never closed. Potential Resource leak."));
|
||||
BT_ResourceLeak.reset(new BuiltinBug(
|
||||
this, "Resource Leak",
|
||||
"Opened File never closed. Potential Resource leak."));
|
||||
BugReport *R = new BugReport(*BT_ResourceLeak,
|
||||
BT_ResourceLeak->getDescription(), N);
|
||||
C.emitReport(R);
|
||||
|
@ -38,7 +38,7 @@ public:
|
||||
|
||||
inline void TaintTesterChecker::initBugType() const {
|
||||
if (!BT)
|
||||
BT.reset(new BugType("Tainted data", "General"));
|
||||
BT.reset(new BugType(this, "Tainted data", "General"));
|
||||
}
|
||||
|
||||
void TaintTesterChecker::checkPostStmt(const Expr *E,
|
||||
|
@ -67,8 +67,8 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition,
|
||||
ExplodedNode *N = Ctx.generateSink();
|
||||
if (N) {
|
||||
if (!BT)
|
||||
BT.reset(
|
||||
new BuiltinBug("Branch condition evaluates to a garbage value"));
|
||||
BT.reset(new BuiltinBug(
|
||||
this, "Branch condition evaluates to a garbage value"));
|
||||
|
||||
// What's going on here: we want to highlight the subexpression of the
|
||||
// condition that is the most likely source of the "uninitialized
|
||||
|
@ -79,7 +79,8 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
|
||||
state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) {
|
||||
if (ExplodedNode *N = C.generateSink()) {
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug("uninitialized variable captured by block"));
|
||||
BT.reset(
|
||||
new BuiltinBug(this, "uninitialized variable captured by block"));
|
||||
|
||||
// Generate a bug report.
|
||||
SmallString<128> buf;
|
||||
|
@ -55,7 +55,8 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
|
||||
return;
|
||||
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug("Result of operation is garbage or undefined"));
|
||||
BT.reset(
|
||||
new BuiltinBug(this, "Result of operation is garbage or undefined"));
|
||||
|
||||
SmallString<256> sbuf;
|
||||
llvm::raw_svector_ostream OS(sbuf);
|
||||
|
@ -50,7 +50,7 @@ UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A,
|
||||
if (!N)
|
||||
return;
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug("Array subscript is undefined"));
|
||||
BT.reset(new BuiltinBug(this, "Array subscript is undefined"));
|
||||
|
||||
// Generate a report for this bug.
|
||||
BugReport *R = new BugReport(*BT, BT->getName(), N);
|
||||
|
@ -54,7 +54,7 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
|
||||
const char *str = "Assigned value is garbage or undefined";
|
||||
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug(str));
|
||||
BT.reset(new BuiltinBug(this, str));
|
||||
|
||||
// Generate a report for this bug.
|
||||
const Expr *ex = 0;
|
||||
|
@ -57,20 +57,14 @@ private:
|
||||
const unsigned numArgs,
|
||||
const unsigned sizeArg,
|
||||
const char *fn) const;
|
||||
void LazyInitialize(OwningPtr<BugType> &BT, const char *name) const {
|
||||
if (BT)
|
||||
return;
|
||||
BT.reset(new BugType(this, name, categories::UnixAPI));
|
||||
}
|
||||
};
|
||||
} //end anonymous namespace
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Utility functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static inline void LazyInitialize(OwningPtr<BugType> &BT,
|
||||
const char *name) {
|
||||
if (BT)
|
||||
return;
|
||||
BT.reset(new BugType(name, categories::UnixAPI));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// "open" (man 2 open)
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -217,7 +211,7 @@ bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C,
|
||||
return false;
|
||||
|
||||
LazyInitialize(BT_mallocZero,
|
||||
"Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
|
||||
"Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
|
||||
|
||||
SmallString<256> S;
|
||||
llvm::raw_svector_ostream os(S);
|
||||
|
@ -165,7 +165,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
|
||||
if (SM.isInSystemHeader(SL) || SM.isInExternCSystemHeader(SL))
|
||||
continue;
|
||||
|
||||
B.EmitBasicReport(D, "Unreachable code", "Dead code",
|
||||
B.EmitBasicReport(D, this, "Unreachable code", "Dead code",
|
||||
"This statement is never executed", DL, SR);
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,8 @@ void VLASizeChecker::reportBug(VLASize_Kind Kind,
|
||||
return;
|
||||
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug("Dangerous variable-length array (VLA) declaration"));
|
||||
BT.reset(new BuiltinBug(
|
||||
this, "Dangerous variable-length array (VLA) declaration"));
|
||||
|
||||
SmallString<256> buf;
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
|
@ -28,6 +28,7 @@ using namespace ento;
|
||||
namespace {
|
||||
|
||||
class WalkAST : public StmtVisitor<WalkAST> {
|
||||
const CheckerBase *Checker;
|
||||
BugReporter &BR;
|
||||
AnalysisDeclContext *AC;
|
||||
|
||||
@ -58,11 +59,10 @@ class WalkAST : public StmtVisitor<WalkAST> {
|
||||
const CallExpr *visitingCallExpr;
|
||||
|
||||
public:
|
||||
WalkAST(BugReporter &br, AnalysisDeclContext *ac)
|
||||
: BR(br),
|
||||
AC(ac),
|
||||
visitingCallExpr(0) {}
|
||||
|
||||
WalkAST(const CheckerBase *checker, BugReporter &br,
|
||||
AnalysisDeclContext *ac)
|
||||
: Checker(checker), BR(br), AC(ac), visitingCallExpr(0) {}
|
||||
|
||||
bool hasWork() const { return !WList.empty(); }
|
||||
|
||||
/// This method adds a CallExpr to the worklist and marks the callee as
|
||||
@ -187,21 +187,19 @@ void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
|
||||
if (isPure) {
|
||||
os << "\n" << "Call pure virtual functions during construction or "
|
||||
<< "destruction may leads undefined behaviour";
|
||||
BR.EmitBasicReport(AC->getDecl(),
|
||||
BR.EmitBasicReport(AC->getDecl(), Checker,
|
||||
"Call pure virtual function during construction or "
|
||||
"Destruction",
|
||||
"Cplusplus",
|
||||
os.str(), CELoc, R);
|
||||
"Cplusplus", os.str(), CELoc, R);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
os << "\n" << "Call virtual functions during construction or "
|
||||
<< "destruction will never go to a more derived class";
|
||||
BR.EmitBasicReport(AC->getDecl(),
|
||||
BR.EmitBasicReport(AC->getDecl(), Checker,
|
||||
"Call virtual function during construction or "
|
||||
"Destruction",
|
||||
"Cplusplus",
|
||||
os.str(), CELoc, R);
|
||||
"Cplusplus", os.str(), CELoc, R);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -215,7 +213,7 @@ class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > {
|
||||
public:
|
||||
void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr,
|
||||
BugReporter &BR) const {
|
||||
WalkAST walker(BR, mgr.getAnalysisDeclContext(RD));
|
||||
WalkAST walker(this, BR, mgr.getAnalysisDeclContext(RD));
|
||||
|
||||
// Check the constructors.
|
||||
for (CXXRecordDecl::ctor_iterator I = RD->ctor_begin(), E = RD->ctor_end();
|
||||
|
@ -3420,7 +3420,8 @@ void BugReporter::FlushReport(BugReport *exampleReport,
|
||||
BugType& BT = exampleReport->getBugType();
|
||||
|
||||
OwningPtr<PathDiagnostic>
|
||||
D(new PathDiagnostic(exampleReport->getDeclWithIssue(),
|
||||
D(new PathDiagnostic(exampleReport->getBugType().getCheckName(),
|
||||
exampleReport->getDeclWithIssue(),
|
||||
exampleReport->getBugType().getName(),
|
||||
exampleReport->getDescription(),
|
||||
exampleReport->getShortDescription(/*Fallback=*/false),
|
||||
@ -3472,13 +3473,21 @@ void BugReporter::FlushReport(BugReport *exampleReport,
|
||||
}
|
||||
|
||||
void BugReporter::EmitBasicReport(const Decl *DeclWithIssue,
|
||||
StringRef name,
|
||||
StringRef category,
|
||||
const CheckerBase *Checker,
|
||||
StringRef Name, StringRef Category,
|
||||
StringRef Str, PathDiagnosticLocation Loc,
|
||||
ArrayRef<SourceRange> Ranges) {
|
||||
EmitBasicReport(DeclWithIssue, Checker->getCheckName(), Name, Category, Str,
|
||||
Loc, Ranges);
|
||||
}
|
||||
void BugReporter::EmitBasicReport(const Decl *DeclWithIssue,
|
||||
CheckName CheckName,
|
||||
StringRef name, StringRef category,
|
||||
StringRef str, PathDiagnosticLocation Loc,
|
||||
ArrayRef<SourceRange> Ranges) {
|
||||
|
||||
// 'BT' is owned by BugReporter.
|
||||
BugType *BT = getBugTypeForName(name, category);
|
||||
BugType *BT = getBugTypeForName(CheckName, name, category);
|
||||
BugReport *R = new BugReport(*BT, str, Loc);
|
||||
R->setDeclWithIssue(DeclWithIssue);
|
||||
for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end();
|
||||
@ -3487,15 +3496,16 @@ void BugReporter::EmitBasicReport(const Decl *DeclWithIssue,
|
||||
emitReport(R);
|
||||
}
|
||||
|
||||
BugType *BugReporter::getBugTypeForName(StringRef name,
|
||||
BugType *BugReporter::getBugTypeForName(CheckName CheckName, StringRef name,
|
||||
StringRef category) {
|
||||
SmallString<136> fullDesc;
|
||||
llvm::raw_svector_ostream(fullDesc) << name << ":" << category;
|
||||
llvm::raw_svector_ostream(fullDesc) << CheckName.getName() << ":" << name
|
||||
<< ":" << category;
|
||||
llvm::StringMapEntry<BugType *> &
|
||||
entry = StrBugTypes.GetOrCreateValue(fullDesc);
|
||||
BugType *BT = entry.getValue();
|
||||
if (!BT) {
|
||||
BT = new BugType(name, category);
|
||||
BT = new BugType(CheckName, name, category);
|
||||
entry.setValue(BT);
|
||||
}
|
||||
return BT;
|
||||
|
@ -22,6 +22,8 @@ StringRef CheckerBase::getTagDescription() const {
|
||||
return "A Checker";
|
||||
}
|
||||
|
||||
CheckName CheckerBase::getCheckName() const { return Name; }
|
||||
|
||||
void Checker<check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
|
||||
check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
|
||||
check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
|
||||
|
@ -106,6 +106,7 @@ void CheckerRegistry::initializeManager(CheckerManager &checkerMgr,
|
||||
// Initialize the CheckerManager with all enabled checkers.
|
||||
for (CheckerInfoSet::iterator
|
||||
i = enabledCheckers.begin(), e = enabledCheckers.end(); i != e; ++i) {
|
||||
checkerMgr.setCurrentCheckName(CheckName((*i)->FullName));
|
||||
(*i)->Initialize(checkerMgr);
|
||||
}
|
||||
}
|
||||
|
@ -106,12 +106,13 @@ void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current,
|
||||
|
||||
PathDiagnostic::~PathDiagnostic() {}
|
||||
|
||||
PathDiagnostic::PathDiagnostic(const Decl *declWithIssue,
|
||||
PathDiagnostic::PathDiagnostic(StringRef CheckName, const Decl *declWithIssue,
|
||||
StringRef bugtype, StringRef verboseDesc,
|
||||
StringRef shortDesc, StringRef category,
|
||||
PathDiagnosticLocation LocationToUnique,
|
||||
const Decl *DeclToUnique)
|
||||
: DeclWithIssue(declWithIssue),
|
||||
: CheckName(CheckName),
|
||||
DeclWithIssue(declWithIssue),
|
||||
BugType(StripTrailingDots(bugtype)),
|
||||
VerboseDesc(StripTrailingDots(verboseDesc)),
|
||||
ShortDesc(StripTrailingDots(shortDesc)),
|
||||
|
Loading…
x
Reference in New Issue
Block a user