r308957 - [analyzer] Further improve suppress-on-sink behavior in incomplete analyses.

Artem Dergachev via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 25 02:25:10 PDT 2017


Author: dergachev
Date: Tue Jul 25 02:25:10 2017
New Revision: 308957

URL: http://llvm.org/viewvc/llvm-project?rev=308957&view=rev
Log:
[analyzer] Further improve suppress-on-sink behavior in incomplete analyses.

If a certain memory leak (or other similar bug) found by the analyzer is known
to be happening only before abnormal termination of the program ("sink", eg.
assertion failure in the code under analysis, or another bug that introduces
undefined behavior), such leak warning is discarded. However, if the analysis
has never reaches completion (due to complexity of the code), it may be
failing to notice the sink.

This commit further extends the partial solution introduced in r290341 to cover
cases when a complicated control flow occurs before encountering a no-return
statement (which anyway inevitably leads to such statement(s)) by traversing
the respective section of the CFG in a depth-first manner. A complete solution
still seems elusive.

rdar://problem/28157554

Differential Revision: https://reviews.llvm.org/D35673

Modified:
    cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp
    cfe/trunk/test/Analysis/max-nodes-suppress-on-sink.c

Modified: cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp?rev=308957&r1=308956&r2=308957&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp Tue Jul 25 02:25:10 2017
@@ -3310,6 +3310,45 @@ static const CFGBlock *findBlockForNode(
   return nullptr;
 }
 
+static bool isDominatedByNoReturnBlocks(const ExplodedNode *N) {
+  const CFG &Cfg = N->getCFG();
+
+  const CFGBlock *StartBlk = findBlockForNode(N);
+  if (!StartBlk)
+    return false;
+  if (StartBlk->hasNoReturnElement())
+    return true;
+
+  llvm::SmallVector<const CFGBlock *, 32> DFSWorkList;
+  llvm::SmallPtrSet<const CFGBlock *, 32> Visited;
+
+  DFSWorkList.push_back(StartBlk);
+  while (!DFSWorkList.empty()) {
+    const CFGBlock *Blk = DFSWorkList.back();
+    DFSWorkList.pop_back();
+    Visited.insert(Blk);
+
+    for (const auto &Succ : Blk->succs()) {
+      if (const CFGBlock *SuccBlk = Succ.getReachableBlock()) {
+        if (SuccBlk == &Cfg.getExit()) {
+          // We seem to be leaving the current CFG.
+          // We're no longer sure what happens next.
+          return false;
+        }
+
+        if (!SuccBlk->hasNoReturnElement() && !Visited.count(SuccBlk)) {
+          // If the block has reachable child blocks that aren't no-return,
+          // add them to the worklist.
+          DFSWorkList.push_back(SuccBlk);
+        }
+      }
+    }
+  }
+
+  // Nothing reached the exit. It can only mean one thing: there's no return.
+  return true;
+}
+
 static BugReport *
 FindReportInEquivalenceClass(BugReportEquivClass& EQ,
                              SmallVectorImpl<BugReport*> &bugReports) {
@@ -3366,9 +3405,8 @@ FindReportInEquivalenceClass(BugReportEq
     // We may be post-dominated in subsequent blocks, or even
     // inter-procedurally. However, it is not clear if more complicated
     // cases are generally worth suppressing.
-    if (const CFGBlock *B = findBlockForNode(errorNode))
-      if (B->hasNoReturnElement())
-        continue;
+    if (isDominatedByNoReturnBlocks(errorNode))
+      continue;
 
     // At this point we know that 'N' is not a sink and it has at least one
     // successor.  Use a DFS worklist to find a non-sink end-of-path node.

Modified: cfe/trunk/test/Analysis/max-nodes-suppress-on-sink.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/max-nodes-suppress-on-sink.c?rev=308957&r1=308956&r2=308957&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/max-nodes-suppress-on-sink.c (original)
+++ cfe/trunk/test/Analysis/max-nodes-suppress-on-sink.c Tue Jul 25 02:25:10 2017
@@ -15,6 +15,8 @@ extern void exit(int) __attribute__ ((__
 
 void clang_analyzer_warnIfReached(void);
 
+int coin();
+
 void test_single_cfg_block_sink() {
   void *p = malloc(1); // no-warning (wherever the leak warning may occur here)
 
@@ -29,3 +31,53 @@ void test_single_cfg_block_sink() {
   // the leak report.
   exit(0);
 }
+
+// A similar test with more complicated control flow before the no-return thing,
+// so that the no-return thing wasn't in the same CFG block.
+void test_more_complex_control_flow_before_sink() {
+  void *p = malloc(1); // no-warning
+
+  clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+  clang_analyzer_warnIfReached(); // no-warning
+
+  if (coin())
+    exit(0);
+  else
+    exit(1);
+}
+
+// A loop before the no-return function, to make sure that
+// the dominated-by-sink analysis doesn't hang.
+void test_loop_before_sink(int n) {
+  void *p = malloc(1); // no-warning
+
+  clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+  clang_analyzer_warnIfReached(); // no-warning
+
+  for (int i = 0; i < n; ++i) {
+  }
+  exit(1);
+}
+
+// We're not sure if this is no-return.
+void test_loop_with_sink(int n) {
+  void *p = malloc(1); // expected-warning at +2{{Potential leak of memory}}
+
+  clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+  clang_analyzer_warnIfReached(); // no-warning
+
+  for (int i = 0; i < n; ++i)
+    if (i == 0)
+      exit(1);
+}
+
+// Handle unreachable blocks correctly.
+void test_unreachable_successor_blocks() {
+  void *p = malloc(1); // no-warning
+
+  clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+  clang_analyzer_warnIfReached(); // no-warning
+
+  if (1) // no-crash
+    exit(1);
+}




More information about the cfe-commits mailing list