summaryrefslogtreecommitdiffstats
path: root/clang/lib/StaticAnalyzer
diff options
context:
space:
mode:
authorArtem Dergachev <artem.dergachev@gmail.com>2019-03-29 22:21:00 +0000
committerArtem Dergachev <artem.dergachev@gmail.com>2019-03-29 22:21:00 +0000
commit6b39f10a00c07bae84d7458e6a616351017bb7c0 (patch)
tree3d67fd6536201a5cf3ac46b8aa72ba9cd574d04a /clang/lib/StaticAnalyzer
parent31a991eeba4f22b9fc0bc6a6b981a600e66f09ac (diff)
downloadbcm5719-llvm-6b39f10a00c07bae84d7458e6a616351017bb7c0.tar.gz
bcm5719-llvm-6b39f10a00c07bae84d7458e6a616351017bb7c0.zip
[analyzer] Introduce a simplified API for adding custom path notes.
Almost all path-sensitive checkers need to tell the user when something specific to that checker happens along the execution path but does not constitute a bug on its own. For instance, a call to operator delete in C++ has consequences that are specific to a use-after-free bug. Deleting an object is not a bug on its own, but when the Analyzer finds an execution path on which a deleted object is used, it'll have to explain to the user when exactly during that path did the deallocation take place. Historically such custom notes were added by implementing "bug report visitors". These visitors were post-processing bug reports by visiting every ExplodedNode along the path and emitting path notes whenever they noticed that a change that is relevant to a bug report occurs within the program state. For example, it emits a "memory is deallocated" note when it notices that a pointer changes its state from "allocated" to "deleted". The "visitor" approach is powerful and efficient but hard to use because such preprocessing implies that the developer first models the effects of the event (say, changes the pointer's state from "allocated" to "deleted" as part of operator delete()'s transfer function) and then forgets what happened and later tries to reverse-engineer itself and figure out what did it do by looking at the report. The proposed approach tries to avoid discarding the information that was available when the transfer function was evaluated. Instead, it allows the developer to capture all the necessary information into a closure that will be automatically invoked later in order to produce the actual note. This should reduce boilerplate and avoid very painful logic duplication. On the technical side, the closure is a lambda that's put into a special kind of a program point tag, and a special bug report visitor visits all nodes in the report and invokes all note-producing closures it finds along the path. For now it is up to the lambda to make sure that the note is actually relevant to the report. For instance, a memory deallocation note would be irrelevant when we're reporting a division by zero bug or if we're reporting a use-after-free of a different, unrelated chunk of memory. The lambda can figure these thing out by looking at the bug report object that's passed into it. A single checker is refactored to make use of the new functionality: MIGChecker. Its program state is trivial, making it an easy testing ground for the first version of the API. Differential Revision: https://reviews.llvm.org/D58367 llvm-svn: 357323
Diffstat (limited to 'clang/lib/StaticAnalyzer')
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp47
-rw-r--r--clang/lib/StaticAnalyzer/Core/BugReporter.cpp1
-rw-r--r--clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp24
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngine.cpp4
4 files changed, 39 insertions, 37 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
index 685fd88b165..b5e78b5d58d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
@@ -80,43 +80,10 @@ public:
checkReturnAux(RS, C);
}
- class Visitor : public BugReporterVisitor {
- public:
- void Profile(llvm::FoldingSetNodeID &ID) const {
- static int X = 0;
- ID.AddPointer(&X);
- }
-
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC, BugReport &R);
- };
};
} // end anonymous namespace
-// FIXME: It's a 'const ParmVarDecl *' but there's no ready-made GDM traits
-// specialization for this sort of types.
-REGISTER_TRAIT_WITH_PROGRAMSTATE(ReleasedParameter, const void *)
-
-std::shared_ptr<PathDiagnosticPiece>
-MIGChecker::Visitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
- BugReport &R) {
- const auto *NewPVD = static_cast<const ParmVarDecl *>(
- N->getState()->get<ReleasedParameter>());
- const auto *OldPVD = static_cast<const ParmVarDecl *>(
- N->getFirstPred()->getState()->get<ReleasedParameter>());
- if (OldPVD == NewPVD)
- return nullptr;
-
- assert(NewPVD && "What is deallocated cannot be un-deallocated!");
- SmallString<64> Str;
- llvm::raw_svector_ostream OS(Str);
- OS << "Value passed through parameter '" << NewPVD->getName()
- << "' is deallocated";
-
- PathDiagnosticLocation Loc =
- PathDiagnosticLocation::create(N->getLocation(), BRC.getSourceManager());
- return std::make_shared<PathDiagnosticEventPiece>(Loc, OS.str());
-}
+REGISTER_TRAIT_WITH_PROGRAMSTATE(ReleasedParameter, bool)
static const ParmVarDecl *getOriginParam(SVal V, CheckerContext &C) {
SymbolRef Sym = V.getAsSymbol();
@@ -195,7 +162,16 @@ void MIGChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const {
if (!PVD)
return;
- C.addTransition(C.getState()->set<ReleasedParameter>(PVD));
+ const NoteTag *T = C.getNoteTag([this, PVD](BugReport &BR) -> std::string {
+ if (&BR.getBugType() != &BT)
+ return "";
+ SmallString<64> Str;
+ llvm::raw_svector_ostream OS(Str);
+ OS << "Value passed through parameter '" << PVD->getName()
+ << "\' is deallocated";
+ return OS.str();
+ });
+ C.addTransition(C.getState()->set<ReleasedParameter>(true), T);
}
// Returns true if V can potentially represent a "successful" kern_return_t.
@@ -260,7 +236,6 @@ void MIGChecker::checkReturnAux(const ReturnStmt *RS, CheckerContext &C) const {
R->addRange(RS->getSourceRange());
bugreporter::trackExpressionValue(N, RS->getRetValue(), *R, false);
- R->addVisitor(llvm::make_unique<Visitor>());
C.emitReport(std::move(R));
}
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
index 1a6314d48b2..b9b579b48bf 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -2612,6 +2612,7 @@ std::pair<BugReport*, std::unique_ptr<VisitorsDiagnosticsTy>> findValidReport(
R->addVisitor(llvm::make_unique<NilReceiverBRVisitor>());
R->addVisitor(llvm::make_unique<ConditionBRVisitor>());
R->addVisitor(llvm::make_unique<CXXSelfAssignmentBRVisitor>());
+ R->addVisitor(llvm::make_unique<TagVisitor>());
BugReporterContext BRC(Reporter, ErrorGraph.BackMap);
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index c86ae43ccd3..2aa5e264139 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -2492,6 +2492,30 @@ FalsePositiveRefutationBRVisitor::VisitNode(const ExplodedNode *N,
return nullptr;
}
+int NoteTag::Kind = 0;
+
+void TagVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
+ static int Tag = 0;
+ ID.AddPointer(&Tag);
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+TagVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
+ BugReport &R) {
+ ProgramPoint PP = N->getLocation();
+ const NoteTag *T = dyn_cast_or_null<NoteTag>(PP.getTag());
+ if (!T)
+ return nullptr;
+
+ if (Optional<std::string> Msg = T->generateMessage(BRC, R)) {
+ PathDiagnosticLocation Loc =
+ PathDiagnosticLocation::create(PP, BRC.getSourceManager());
+ return std::make_shared<PathDiagnosticEventPiece>(Loc, *Msg);
+ }
+
+ return nullptr;
+}
+
void FalsePositiveRefutationBRVisitor::Profile(
llvm::FoldingSetNodeID &ID) const {
static int Tag = 0;
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index fb86ceab135..e36820fa2a4 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -201,7 +201,9 @@ ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU,
svalBuilder(StateMgr.getSValBuilder()),
ObjCNoRet(mgr.getASTContext()),
BR(mgr, *this),
- VisitedCallees(VisitedCalleesIn), HowToInline(HowToInlineIn) {
+ VisitedCallees(VisitedCalleesIn),
+ HowToInline(HowToInlineIn),
+ NoteTags(G.getAllocator()) {
unsigned TrimInterval = mgr.options.GraphTrimInterval;
if (TrimInterval != 0) {
// Enable eager node reclamation when constructing the ExplodedGraph.
OpenPOWER on IntegriCloud