Skip to content

Commit 8cd2ee1

Browse files
committedJun 4, 2018
[analyzer] False positive refutation with Z3
Summary: This is a prototype of a bug reporter visitor that invalidates bug reports by re-checking constraints of certain states on the bug path using the Z3 constraint manager backend. The functionality is available under the `crosscheck-with-z3` analyzer config flag. Reviewers: george.karpenkov, NoQ, dcoughlin, rnkovacs Reviewed By: george.karpenkov Subscribers: rnkovacs, NoQ, george.karpenkov, dcoughlin, xbolva00, ddcc, mikhail.ramalho, MTC, fhahn, whisperity, baloghadamsoftware, szepet, a.sidorin, gsd, dkrupp, xazax.hun, cfe-commits Differential Revision: https://reviews.llvm.org/D45517 llvm-svn: 333903
1 parent 771e3be commit 8cd2ee1

File tree

6 files changed

+139
-1
lines changed

6 files changed

+139
-1
lines changed
 

Diff for: ‎clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h

+10
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,9 @@ class AnalyzerOptions : public RefCountedBase<AnalyzerOptions> {
280280
/// \sa shouldSuppressFromCXXStandardLibrary
281281
Optional<bool> SuppressFromCXXStandardLibrary;
282282

283+
/// \sa shouldCrosscheckWithZ3
284+
Optional<bool> CrosscheckWithZ3;
285+
283286
/// \sa reportIssuesInMainSourceFile
284287
Optional<bool> ReportIssuesInMainSourceFile;
285288

@@ -575,6 +578,13 @@ class AnalyzerOptions : public RefCountedBase<AnalyzerOptions> {
575578
/// which accepts the values "true" and "false".
576579
bool shouldSuppressFromCXXStandardLibrary();
577580

581+
/// Returns whether bug reports should be crosschecked with the Z3
582+
/// constraint manager backend.
583+
///
584+
/// This is controlled by the 'crosscheck-with-z3' config option,
585+
/// which accepts the values "true" and "false".
586+
bool shouldCrosscheckWithZ3();
587+
578588
/// Returns whether or not the diagnostic report should be always reported
579589
/// in the main source file and not the headers.
580590
///

Diff for: ‎clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h

+22
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H
1717

1818
#include "clang/Basic/LLVM.h"
19+
#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h"
1920
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
2021
#include "llvm/ADT/FoldingSet.h"
2122
#include "llvm/ADT/STLExtras.h"
@@ -359,6 +360,27 @@ class TaintBugVisitor final : public BugReporterVisitorImpl<TaintBugVisitor> {
359360
BugReport &BR) override;
360361
};
361362

363+
/// The bug visitor will walk all the nodes in a path and collect all the
364+
/// constraints. When it reaches the root node, will create a refutation
365+
/// manager and check if the constraints are satisfiable
366+
class FalsePositiveRefutationBRVisitor final
367+
: public BugReporterVisitorImpl<FalsePositiveRefutationBRVisitor> {
368+
private:
369+
/// Holds the constraints in a given path
370+
// TODO: should we use a set?
371+
llvm::SmallVector<ConstraintRangeTy, 32> Constraints;
372+
373+
public:
374+
FalsePositiveRefutationBRVisitor() = default;
375+
376+
void Profile(llvm::FoldingSetNodeID &ID) const override;
377+
378+
std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
379+
const ExplodedNode *PrevN,
380+
BugReporterContext &BRC,
381+
BugReport &BR) override;
382+
};
383+
362384
namespace bugreporter {
363385

364386
/// Attempts to add visitors to trace a null or undefined value back to its

Diff for: ‎clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,12 @@ bool AnalyzerOptions::shouldSuppressFromCXXStandardLibrary() {
296296
/* Default = */ true);
297297
}
298298

299+
bool AnalyzerOptions::shouldCrosscheckWithZ3() {
300+
return getBooleanOption(CrosscheckWithZ3,
301+
"crosscheck-with-z3",
302+
/* Default = */ false);
303+
}
304+
299305
bool AnalyzerOptions::shouldReportIssuesInMainSourceFile() {
300306
return getBooleanOption(ReportIssuesInMainSourceFile,
301307
"report-in-main-source-file",

Diff for: ‎clang/lib/StaticAnalyzer/Core/BugReporter.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -3143,10 +3143,15 @@ bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD,
31433143
PathDiagnosticBuilder PDB(*this, R, ErrorGraph.BackMap, &PC);
31443144
const ExplodedNode *N = ErrorGraph.ErrorNode;
31453145

3146+
// Register refutation visitors first, if they mark the bug invalid no
3147+
// further analysis is required
3148+
R->addVisitor(llvm::make_unique<LikelyFalsePositiveSuppressionBRVisitor>());
3149+
if (getAnalyzerOptions().shouldCrosscheckWithZ3())
3150+
R->addVisitor(llvm::make_unique<FalsePositiveRefutationBRVisitor>());
3151+
31463152
// Register additional node visitors.
31473153
R->addVisitor(llvm::make_unique<NilReceiverBRVisitor>());
31483154
R->addVisitor(llvm::make_unique<ConditionBRVisitor>());
3149-
R->addVisitor(llvm::make_unique<LikelyFalsePositiveSuppressionBRVisitor>());
31503155
R->addVisitor(llvm::make_unique<CXXSelfAssignmentBRVisitor>());
31513156

31523157
BugReport::VisitorList visitors;

Diff for: ‎clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp

+44
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
4545
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
4646
#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
47+
#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h"
4748
#include "llvm/ADT/ArrayRef.h"
4849
#include "llvm/ADT/None.h"
4950
#include "llvm/ADT/Optional.h"
@@ -2354,3 +2355,46 @@ TaintBugVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN,
23542355

23552356
return std::make_shared<PathDiagnosticEventPiece>(L, "Taint originated here");
23562357
}
2358+
2359+
static bool
2360+
areConstraintsUnfeasible(BugReporterContext &BRC,
2361+
const llvm::SmallVector<ConstraintRangeTy, 32> &Cs) {
2362+
// Create a refutation manager
2363+
std::unique_ptr<ConstraintManager> RefutationMgr = CreateZ3ConstraintManager(
2364+
BRC.getStateManager(), BRC.getStateManager().getOwningEngine());
2365+
2366+
SMTConstraintManager *SMTRefutationMgr =
2367+
static_cast<SMTConstraintManager *>(RefutationMgr.get());
2368+
2369+
// Add constraints to the solver
2370+
for (const auto &C : Cs)
2371+
SMTRefutationMgr->addRangeConstraints(C);
2372+
2373+
// And check for satisfiability
2374+
return SMTRefutationMgr->isModelFeasible().isConstrainedFalse();
2375+
}
2376+
2377+
std::shared_ptr<PathDiagnosticPiece>
2378+
FalsePositiveRefutationBRVisitor::VisitNode(const ExplodedNode *N,
2379+
const ExplodedNode *PrevN,
2380+
BugReporterContext &BRC,
2381+
BugReport &BR) {
2382+
// Collect the constraint for the current state
2383+
const ConstraintRangeTy &CR = N->getState()->get<ConstraintRange>();
2384+
Constraints.push_back(CR);
2385+
2386+
// If there are no predecessor, we reached the root node. In this point,
2387+
// a new refutation manager will be created and the path will be checked
2388+
// for reachability
2389+
if (PrevN->pred_size() == 0 && areConstraintsUnfeasible(BRC, Constraints)) {
2390+
BR.markInvalid("Infeasible constraints", N->getLocationContext());
2391+
}
2392+
2393+
return nullptr;
2394+
}
2395+
2396+
void FalsePositiveRefutationBRVisitor::Profile(
2397+
llvm::FoldingSetNodeID &ID) const {
2398+
static int Tag = 0;
2399+
ID.AddPointer(&Tag);
2400+
}

Diff for: ‎clang/test/Analysis/z3-crosscheck.c

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -DNO_CROSSCHECK -verify %s
2+
// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config crosscheck-with-z3=true -verify %s
3+
// REQUIRES: z3
4+
5+
int foo(int x)
6+
{
7+
int *z = 0;
8+
if ((x & 1) && ((x & 1) ^ 1))
9+
#ifdef NO_CROSSCHECK
10+
return *z; // expected-warning {{Dereference of null pointer (loaded from variable 'z')}}
11+
#else
12+
return *z; // no-warning
13+
#endif
14+
return 0;
15+
}
16+
17+
void g(int d);
18+
19+
void f(int *a, int *b) {
20+
int c = 5;
21+
if ((a - b) == 0)
22+
c = 0;
23+
if (a != b)
24+
#ifdef NO_CROSSCHECK
25+
g(3 / c); // expected-warning {{Division by zero}}
26+
#else
27+
g(3 / c); // no-warning
28+
#endif
29+
}
30+
31+
_Bool nondet_bool();
32+
33+
void h(int d) {
34+
int x, y, k, z = 1;
35+
#ifdef NO_CROSSCHECK
36+
while (z < k) { // expected-warning {{The right operand of '<' is a garbage value}}
37+
#else
38+
while (z < k) { // expected-warning {{The right operand of '<' is a garbage value}}
39+
#endif
40+
z = 2 * z;
41+
}
42+
}
43+
44+
void i() {
45+
_Bool c = nondet_bool();
46+
if (c) {
47+
h(1);
48+
} else {
49+
h(2);
50+
}
51+
}

0 commit comments

Comments
 (0)
Please sign in to comment.