[analyzer] BugReporter - more precise tracking of C++ references
When BugReporter tracks C++ references involved in a null pointer violation, we
want to differentiate between a null reference and a reference to a null pointer. In the
first case, we want to track the region for the reference location; in the second, we want
to track the null pointer.
In addition, the core creates CXXTempObjectRegion to represent the location of the
C++ reference, so teach FindLastStoreBRVisitor about it.
This helps null pointer suppression to kick in.
(Patch by Anna and Jordan.)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@176969 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 517c1a1..56d6d26 100644
--- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -470,6 +470,11 @@
IsParam = true;
}
}
+
+ // If this is a CXXTempObjectRegion, the Expr responsible for its creation
+ // is wrapped inside of it.
+ if (const CXXTempObjectRegion *TmpR = dyn_cast<CXXTempObjectRegion>(R))
+ InitE = TmpR->getExpr();
}
if (!StoreSite)
@@ -756,6 +761,27 @@
return 0;
}
+static const MemRegion *getLocationRegionIfReference(const Expr *E,
+ const ExplodedNode *N) {
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) {
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+ if (!VD->getType()->isReferenceType())
+ return 0;
+ ProgramStateManager &StateMgr = N->getState()->getStateManager();
+ MemRegionManager &MRMgr = StateMgr.getRegionManager();
+ return MRMgr.getVarRegion(VD, N->getLocationContext());
+ }
+ }
+
+ // FIXME: This does not handle other kinds of null references,
+ // for example, references from FieldRegions:
+ // struct Wrapper { int &ref; };
+ // Wrapper w = { *(int *)0 };
+ // w.ref = 1;
+
+ return 0;
+}
+
bool bugreporter::trackNullOrUndefValue(const ExplodedNode *ErrorNode,
const Stmt *S,
BugReport &report, bool IsArg) {
@@ -804,33 +830,37 @@
if (Inner && ExplodedGraph::isInterestingLValueExpr(Inner)) {
const MemRegion *R = 0;
- // First check if this is a DeclRefExpr for a C++ reference type.
- // For those, we want the location of the reference.
- if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Inner)) {
- if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
- if (VD->getType()->isReferenceType()) {
- ProgramStateManager &StateMgr = state->getStateManager();
- MemRegionManager &MRMgr = StateMgr.getRegionManager();
- R = MRMgr.getVarRegion(VD, N->getLocationContext());
- }
+ // Find the ExplodedNode where the lvalue (the value of 'Ex')
+ // was computed. We need this for getting the location value.
+ const ExplodedNode *LVNode = N;
+ while (LVNode) {
+ if (Optional<PostStmt> P = LVNode->getLocation().getAs<PostStmt>()) {
+ if (P->getStmt() == Inner)
+ break;
}
+ LVNode = LVNode->getFirstPred();
}
-
- // For all other cases, find the location by scouring the ExplodedGraph.
- if (!R) {
- // Find the ExplodedNode where the lvalue (the value of 'Ex')
- // was computed. We need this for getting the location value.
- const ExplodedNode *LVNode = N;
- while (LVNode) {
- if (Optional<PostStmt> P = LVNode->getLocation().getAs<PostStmt>()) {
- if (P->getStmt() == Inner)
- break;
- }
- LVNode = LVNode->getFirstPred();
- }
- assert(LVNode && "Unable to find the lvalue node.");
- ProgramStateRef LVState = LVNode->getState();
+ assert(LVNode && "Unable to find the lvalue node.");
+ ProgramStateRef LVState = LVNode->getState();
+ SVal LVal = LVState->getSVal(Inner, LVNode->getLocationContext());
+
+ if (LVState->isNull(LVal).isConstrainedTrue()) {
+ // In case of C++ references, we want to differentiate between a null
+ // reference and reference to null pointer.
+ // If the LVal is null, check if we are dealing with null reference.
+ // For those, we want to track the location of the reference.
+ if (const MemRegion *RR = getLocationRegionIfReference(Inner, N))
+ R = RR;
+ } else {
R = LVState->getSVal(Inner, LVNode->getLocationContext()).getAsRegion();
+
+ // If this is a C++ reference to a null pointer, we are tracking the
+ // pointer. In additon, we should find the store at which the reference
+ // got initialized.
+ if (const MemRegion *RR = getLocationRegionIfReference(Inner, N)) {
+ if (Optional<KnownSVal> KV = LVal.getAs<KnownSVal>())
+ report.addVisitor(new FindLastStoreBRVisitor(*KV, RR));
+ }
}
if (R) {
diff --git a/test/Analysis/diagnostics/deref-track-symbolic-region.cpp b/test/Analysis/diagnostics/deref-track-symbolic-region.cpp
index e166109..6d34841 100644
--- a/test/Analysis/diagnostics/deref-track-symbolic-region.cpp
+++ b/test/Analysis/diagnostics/deref-track-symbolic-region.cpp
@@ -25,4 +25,19 @@
extern void use(int &ref);
use(ref); // expected-warning{{Forming reference to null pointer}}
// expected-note@-1{{Forming reference to null pointer}}
+}
+
+int testRefToNullPtr() {
+ int *p = 0; // expected-note {{'p' initialized to a null pointer value}}
+ int *const &p2 = p; // expected-note{{'p2' initialized here}}
+ int *p3 = p2; // expected-note {{'p3' initialized to a null pointer value}}
+ return *p3; // expected-warning {{Dereference of null pointer}}
+ // expected-note@-1{{Dereference of null pointer}}
+}
+
+int testRefToNullPtr2() {
+ int *p = 0; // expected-note {{'p' initialized to a null pointer value}}
+ int *const &p2 = p;// expected-note{{'p2' initialized here}}
+ return *p2; //expected-warning {{Dereference of null pointer}}
+ // expected-note@-1{{Dereference of null pointer}}
}
\ No newline at end of file
diff --git a/test/Analysis/inlining/false-positive-suppression.cpp b/test/Analysis/inlining/false-positive-suppression.cpp
index f27c7cf..613f421 100644
--- a/test/Analysis/inlining/false-positive-suppression.cpp
+++ b/test/Analysis/inlining/false-positive-suppression.cpp
@@ -125,3 +125,52 @@
#endif
}
}
+
+class X{
+public:
+ void get();
+};
+
+X *getNull() {
+ return 0;
+}
+
+void deref1(X *const &p) {
+ return p->get();
+ #ifndef SUPPRESSED
+ // expected-warning@-2 {{Called C++ object pointer is null}}
+ #endif
+}
+
+void test1() {
+ return deref1(getNull());
+}
+
+void deref2(X *p3) {
+ p3->get();
+ #ifndef SUPPRESSED
+ // expected-warning@-2 {{Called C++ object pointer is null}}
+ #endif
+}
+
+void pass2(X *const &p2) {
+ deref2(p2);
+}
+
+void test2() {
+ pass2(getNull());
+}
+
+void deref3(X *const &p2) {
+ X *p3 = p2;
+ p3->get();
+ #ifndef SUPPRESSED
+ // expected-warning@-2 {{Called C++ object pointer is null}}
+ #endif
+}
+
+void test3() {
+ deref3(getNull());
+}
+
+