[pushed] analyzer: stop exploring the path after certain diagnostics [PR108830]

Message ID 20230221220733.2068735-1-dmalcolm@redhat.com
State Accepted
Headers
Series [pushed] analyzer: stop exploring the path after certain diagnostics [PR108830] |

Checks

Context Check Description
snail/gcc-patch-check success Github commit url

Commit Message

David Malcolm Feb. 21, 2023, 10:07 p.m. UTC
  PR analyzer/108830 reports a situation in which there are lots of
followup -Wanalyzer-null-dereference warnings after the first access of
a NULL pointer, leading to very noisy output from -fanalyzer.

The analyzer's logic for stopping emitting multiple warnings from a
state machine doesn't quite work for NULL pointers: it attempts to
transition the malloc state machine's NULL pointer to the "stop" state,
which doesn't seem to make much sense in retrospect, and seems to get
confused over types.

Similarly, poisoned_value_diagnostic can be very noisy for uninit
variables, emitting a warning for every access to an uninitialized
variable.  In theory, region_model::check_for_poison makes some attempts
to suppress followups, but only for the symbolic value itself; if the
user's code keeps accessing the same region, we would get a warning on
each one.  For example, this showed up in Doom's s_sound.c where there
were 7 followup uninit warnings after the first uninit warning in
"S_ChangeMusic".

This patch adds an extra mechanism, giving pending diagnostics the
option of stopping the analysis of an execution path if they're saved
for emission on it, and turning this on for these warnings:
  -Wanalyzer-null-dereference
  -Wanalyzer-null-argument
  -Wanalyzer-use-after-free
  -Wanalyzer-use-of-pointer-in-stale-stack-frame
  -Wanalyzer-use-of-uninitialized-value

Doing so should hopefully reduce the cascades of diagnostics that
-fanalyzer can sometimes emit.

I added a -fno-analyzer-suppress-followups for the cases where you
really want the followup warnings (e.g. in some DejaGnu tests, and
for microbenchmarks of UB detection, such as PR analyzer/104224).

Integration testing shows this patch reduces the number of probable
false positives reported by 94, and finds one more true positive:

Comparison: 9.34% -> 10.91%
  GOOD:  66 ->  67  (+1)
   BAD: 641 -> 547 (-94)

where the affected warnings/projects are:

  -Wanalyzer-null-dereference: 0.00% GOOD: 0 BAD: 269 -> 239 (-30)
     Unclassified: 257 -> 228 (-29)
                  apr-1.7.0:  12 ->   5  (-7)
                       doom:   1 ->   0  (-1)
              haproxy-2.7.1:  47 ->  41  (-6)
       ImageMagick-7.1.0-57:  13 ->   9  (-4)
                 qemu-7.2.0: 165 -> 154 (-11)

      Known false: 7 -> 6 (-1)
                   xz-5.4.0:   4 ->   3  (-1)

  -Wanalyzer-use-of-uninitialized-value: 0.00% GOOD: 0 BAD: 143 -> 80 (-63)
      Known false: 47 -> 16 (-31)
                       doom: 42 -> 11 (-31)

     Unclassified: 96 -> 64 (-32)
              coreutils-9.1: 14 -> 10  (-4)
              haproxy-2.7.1: 29 -> 23  (-6)
                 qemu-7.2.0: 48 -> 26 (-22)

  -Wanalyzer-null-argument: 0.00% -> 2.33% GOOD: 0 -> 1 (+1) BAD: 43 -> 42 (-1)
     Unclassified: 39 -> 38 (-1)
      due to coreutils-9.1: 9 -> 8 (-1)

    True positive: 0 -> 1 (+1)
      (in haproxy-2.7.1)

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r13-6267-g8f6369157917a4.

gcc/analyzer/ChangeLog:
	PR analyzer/108830
	* analyzer.opt (fanalyzer-suppress-followups): New option.
	* engine.cc (impl_region_model_context::warn): Terminate the path
	if the diagnostic's terminate_path_p vfunc returns true and
	-fanalyzer-suppress-followups is true (the default).
	(impl_sm_context::warn): Likewise, for both overloads.
	* pending-diagnostic.h (pending_diagnostic::terminate_path_p): New
	vfunc.
	* program-state.cc (program_state::on_edge): Terminate the path if
	the ctxt requests it during updating the edge.
	* region-model.cc (poisoned_value_diagnostic::terminate_path_p):
	New vfunc.
	* sm-malloc.cc (null_deref::terminate_path_p): New vfunc.
	(null_arg::terminate_path_p): New vfunc.

gcc/ChangeLog:
	PR analyzer/108830
	* doc/invoke.texi: Document -fno-analyzer-suppress-followups.

gcc/testsuite/ChangeLog:
	PR analyzer/108830
	* gcc.dg/analyzer/attribute-nonnull.c: Update for
	-Wanalyzer-use-of-uninitialized-value terminating analysis along
	a path.
	* gcc.dg/analyzer/call-summaries-2.c: Likewise.
	* gcc.dg/analyzer/data-model-1.c: Likewise.
	* gcc.dg/analyzer/data-model-5.c: Likewise.
	* gcc.dg/analyzer/doom-s_sound-pr108867.c: New test.
	* gcc.dg/analyzer/memset-CVE-2017-18549-1.c: Add
	-fno-analyzer-suppress-followups.
	* gcc.dg/analyzer/null-deref-pr108830.c: New test.
	* gcc.dg/analyzer/pipe-1.c: Add -fno-analyzer-suppress-followups.
	* gcc.dg/analyzer/pipe-void-return.c: Likewise.
	* gcc.dg/analyzer/pipe2-1.c: Likewise.
	* gcc.dg/analyzer/pr101547.c: Update for
	-Wanalyzer-use-of-uninitialized-value terminating analysis along
	a path.
	* gcc.dg/analyzer/pr101875.c: Likewise.
	* gcc.dg/analyzer/pr104224-split.c: New test, based on...
	* gcc.dg/analyzer/pr104224.c: Add
	-fno-analyzer-suppress-followups.
	* gcc.dg/analyzer/realloc-2.c: Add
	-fno-analyzer-suppress-followups.
	* gcc.dg/analyzer/realloc-3.c: Likewise.
	* gcc.dg/analyzer/realloc-5.c: Likewise.
	* gcc.dg/analyzer/stdarg-1-ms_abi.c: Likewise.
	* gcc.dg/analyzer/stdarg-1-sysv_abi.c: Likewise.
	* gcc.dg/analyzer/stdarg-1.c: Likewise.
	* gcc.dg/analyzer/symbolic-1.c: Likewise.
	* gcc.dg/analyzer/symbolic-7.c: Update for
	-Wanalyzer-use-of-uninitialized-value terminating analysis along a
	path.
	* gcc.dg/analyzer/uninit-4.c: Likewise.
	* gcc.dg/analyzer/uninit-8.c: New test.
	* gcc.dg/analyzer/uninit-pr94713.c: Update for
	-Wanalyzer-use-of-uninitialized-value terminating analysis along a
	path.
	* gcc.dg/analyzer/zlib-6a.c: Add -fno-analyzer-suppress-followups.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
 gcc/analyzer/analyzer.opt                     |   4 +
 gcc/analyzer/engine.cc                        |  29 +-
 gcc/analyzer/pending-diagnostic.h             |   4 +
 gcc/analyzer/program-state.cc                 |  28 +-
 gcc/analyzer/region-model.cc                  |   2 +
 gcc/analyzer/sm-malloc.cc                     |   4 +
 gcc/doc/invoke.texi                           |  25 +
 .../gcc.dg/analyzer/attribute-nonnull.c       |  23 +-
 .../gcc.dg/analyzer/call-summaries-2.c        |  15 +-
 gcc/testsuite/gcc.dg/analyzer/data-model-1.c  |  19 +-
 gcc/testsuite/gcc.dg/analyzer/data-model-5.c  |   4 -
 .../gcc.dg/analyzer/doom-s_sound-pr108867.c   | 653 ++++++++++++++++++
 .../gcc.dg/analyzer/memset-CVE-2017-18549-1.c |   2 +
 .../gcc.dg/analyzer/null-deref-pr108830.c     |  94 +++
 gcc/testsuite/gcc.dg/analyzer/pipe-1.c        |   2 +
 .../gcc.dg/analyzer/pipe-void-return.c        |   2 +
 gcc/testsuite/gcc.dg/analyzer/pipe2-1.c       |   2 +
 gcc/testsuite/gcc.dg/analyzer/pr101547.c      |  10 +-
 gcc/testsuite/gcc.dg/analyzer/pr101875.c      |  14 +-
 .../gcc.dg/analyzer/pr104224-split.c          | 136 ++++
 gcc/testsuite/gcc.dg/analyzer/pr104224.c      |   2 +
 gcc/testsuite/gcc.dg/analyzer/realloc-2.c     |   2 +
 gcc/testsuite/gcc.dg/analyzer/realloc-3.c     |   2 +
 gcc/testsuite/gcc.dg/analyzer/realloc-5.c     |   2 +
 .../gcc.dg/analyzer/stdarg-1-ms_abi.c         |   1 +
 .../gcc.dg/analyzer/stdarg-1-sysv_abi.c       |   1 +
 gcc/testsuite/gcc.dg/analyzer/stdarg-1.c      |   2 +
 gcc/testsuite/gcc.dg/analyzer/symbolic-1.c    |   2 +
 gcc/testsuite/gcc.dg/analyzer/symbolic-7.c    |  14 +-
 gcc/testsuite/gcc.dg/analyzer/uninit-4.c      |  18 +-
 gcc/testsuite/gcc.dg/analyzer/uninit-8.c      |  73 ++
 .../gcc.dg/analyzer/uninit-pr94713.c          |  10 +-
 gcc/testsuite/gcc.dg/analyzer/zlib-6a.c       |   2 +
 33 files changed, 1157 insertions(+), 46 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/doom-s_sound-pr108867.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/null-deref-pr108830.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/pr104224-split.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/uninit-8.c
  

Patch

diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
index b4dcdb8f451..9d1a937e403 100644
--- a/gcc/analyzer/analyzer.opt
+++ b/gcc/analyzer/analyzer.opt
@@ -262,6 +262,10 @@  fanalyzer-state-merge
 Common Var(flag_analyzer_state_merge) Init(1)
 Merge similar-enough states during analysis.
 
+fanalyzer-suppress-followups
+Common Var(flag_analyzer_suppress_followups) Init(1)
+Stop exploring an execution path after certain diagnostics.
+
 fanalyzer-transitivity
 Common Var(flag_analyzer_transitivity) Init(0)
 Enable transitivity of constraints during analysis.
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 24ded267019..a5965c2b8ff 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -125,11 +125,20 @@  impl_region_model_context::warn (std::unique_ptr<pending_diagnostic> d)
       return false;
     }
   if (m_eg)
-    return m_eg->get_diagnostic_manager ().add_diagnostic
-      (m_enode_for_diag, m_enode_for_diag->get_supernode (),
-       m_stmt, m_stmt_finder, std::move (d));
-  else
-    return false;
+    {
+      bool terminate_path = d->terminate_path_p ();
+      if (m_eg->get_diagnostic_manager ().add_diagnostic
+	  (m_enode_for_diag, m_enode_for_diag->get_supernode (),
+	   m_stmt, m_stmt_finder, std::move (d)))
+	{
+	  if (m_path_ctxt
+	      && terminate_path
+	      && flag_analyzer_suppress_followups)
+	    m_path_ctxt->terminate_path ();
+	  return true;
+	}
+    }
+  return false;
 }
 
 void
@@ -378,9 +387,14 @@  public:
       = (var
 	 ? m_old_smap->get_state (var_old_sval, m_eg.get_ext_state ())
 	 : m_old_smap->get_global_state ());
+    bool terminate_path = d->terminate_path_p ();
     m_eg.get_diagnostic_manager ().add_diagnostic
       (&m_sm, m_enode_for_diag, snode, stmt, m_stmt_finder,
        var, var_old_sval, current, std::move (d));
+    if (m_path_ctxt
+	&& terminate_path
+	&& flag_analyzer_suppress_followups)
+      m_path_ctxt->terminate_path ();
   }
 
   void warn (const supernode *snode, const gimple *stmt,
@@ -393,9 +407,14 @@  public:
       = (sval
 	 ? m_old_smap->get_state (sval, m_eg.get_ext_state ())
 	 : m_old_smap->get_global_state ());
+    bool terminate_path = d->terminate_path_p ();
     m_eg.get_diagnostic_manager ().add_diagnostic
       (&m_sm, m_enode_for_diag, snode, stmt, m_stmt_finder,
        NULL_TREE, sval, current, std::move (d));
+    if (m_path_ctxt
+	&& terminate_path
+	&& flag_analyzer_suppress_followups)
+      m_path_ctxt->terminate_path ();
   }
 
   /* Hook for picking more readable trees for SSA names of temporaries,
diff --git a/gcc/analyzer/pending-diagnostic.h b/gcc/analyzer/pending-diagnostic.h
index d9e9e7f8905..6423c8be4ea 100644
--- a/gcc/analyzer/pending-diagnostic.h
+++ b/gcc/analyzer/pending-diagnostic.h
@@ -173,6 +173,10 @@  class pending_diagnostic
      having to generate feasible execution paths for them).  */
   virtual int get_controlling_option () const = 0;
 
+  /* Vfunc to give the diagnostic the chance to terminate the execution
+     path being explored.  By default, don't terminate the path.  */
+  virtual bool terminate_path_p () const { return false; }
+
   /* Vfunc for emitting the diagnostic.  The rich_location will have been
      populated with a diagnostic_path.
      Return true if a diagnostic is actually emitted.  */
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index 9a1a8cd8e52..8dade4b5b3e 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -1105,6 +1105,27 @@  program_state::on_edge (exploded_graph &eg,
 			const superedge *succ,
 			uncertainty_t *uncertainty)
 {
+  class my_path_context : public path_context
+  {
+  public:
+    my_path_context (bool &terminated) : m_terminated (terminated) {}
+    void bifurcate (std::unique_ptr<custom_edge_info>) final override
+    {
+      gcc_unreachable ();
+    }
+
+    void terminate_path () final override
+    {
+      m_terminated = true;
+    }
+
+    bool terminate_path_p () const final override
+    {
+      return m_terminated;
+    }
+    bool &m_terminated;
+  };
+
   /* Update state.  */
   const program_point &point = enode->get_point ();
   const gimple *last_stmt = point.get_supernode ()->get_last_stmt ();
@@ -1117,11 +1138,12 @@  program_state::on_edge (exploded_graph &eg,
      Adding the relevant conditions for the edge could also trigger
      sm-state transitions (e.g. transitions due to ptrs becoming known
      to be NULL or non-NULL) */
-
+  bool terminated = false;
+  my_path_context path_ctxt (terminated);
   impl_region_model_context ctxt (eg, enode,
 				  &enode->get_state (),
 				  this,
-				  uncertainty, NULL,
+				  uncertainty, &path_ctxt,
 				  last_stmt);
   if (!m_region_model->maybe_update_for_edge (*succ,
 					      last_stmt,
@@ -1134,6 +1156,8 @@  program_state::on_edge (exploded_graph &eg,
 		     succ->m_dest->m_index);
       return false;
     }
+  if (terminated)
+    return false;
 
   program_state::detect_leaks (enode->get_state (), *this,
 			       NULL, eg.get_ext_state (),
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index e3de74bbf45..f844b519f61 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -505,6 +505,8 @@  public:
       }
   }
 
+  bool terminate_path_p () const final override { return true; }
+
   bool emit (rich_location *rich_loc) final override
   {
     switch (m_pkind)
diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc
index c24fe737481..1ea9b30fa13 100644
--- a/gcc/analyzer/sm-malloc.cc
+++ b/gcc/analyzer/sm-malloc.cc
@@ -1150,6 +1150,8 @@  public:
     return OPT_Wanalyzer_null_dereference;
   }
 
+  bool terminate_path_p () const final override { return true; }
+
   bool emit (rich_location *rich_loc) final override
   {
     /* CWE-476: NULL Pointer Dereference.  */
@@ -1203,6 +1205,8 @@  public:
     return OPT_Wanalyzer_null_argument;
   }
 
+  bool terminate_path_p () const final override { return true; }
+
   bool emit (rich_location *rich_loc) final override
   {
     /* CWE-476: NULL Pointer Dereference.  */
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 078d29d5313..bcf8510945b 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -428,6 +428,7 @@  Objective-C and Objective-C++ Dialects}.
 -fanalyzer-fine-grained @gol
 -fno-analyzer-state-merge @gol
 -fno-analyzer-state-purge @gol
+-fno-analyzer-suppress-followups @gol
 -fanalyzer-transitivity @gol
 -fno-analyzer-undo-inlining @gol
 -fanalyzer-verbose-edges @gol
@@ -11012,6 +11013,30 @@  and which aren't relevant to leak analysis.
 With @option{-fno-analyzer-state-purge} this purging of state can
 be suppressed, for debugging state-handling issues.
 
+@item -fno-analyzer-suppress-followups
+@opindex fanalyzer-suppress-followups
+@opindex fno-analyzer-suppress-followups
+This option is intended for analyzer developers.
+
+By default the analyzer will stop exploring an execution path after
+encountering certain diagnostics, in order to avoid potentially issuing a
+cascade of follow-up diagnostics.
+
+The diagnostics that terminate analysis along a path are:
+
+@itemize
+@item @option{-Wanalyzer-null-argument}
+@item @option{-Wanalyzer-null-dereference}
+@item @option{-Wanalyzer-use-after-free}
+@item @option{-Wanalyzer-use-of-pointer-in-stale-stack-frame}
+@item @option{-Wanalyzer-use-of-uninitialized-value}
+@end itemize
+
+With @option{-fno-analyzer-suppress-followups} the analyzer will
+continue to explore such paths even after such diagnostics, which may
+be helpful for debugging issues in the analyzer, or for microbenchmarks
+for detecting undefined behavior.
+
 @item -fanalyzer-transitivity
 @opindex fanalyzer-transitivity
 @opindex fno-analyzer-transitivity
diff --git a/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c b/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c
index 7c71a71c930..5700256c06a 100644
--- a/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c
+++ b/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c
@@ -16,8 +16,6 @@  void test_1 (void *p, void *q, void *r)
   foo(p, q, r);
   foo(NULL, q, r); /* { dg-warning "use of NULL where non-null expected" "warning" } */
   /* { dg-message "argument 1 NULL where non-null expected" "note" { target *-*-* } .-1 } */
-  foo(p, NULL, r);
-  foo(p, q, NULL); /* { dg-warning "use of NULL where non-null expected" } */
 }
 
 void test_1a (void *q, void *r)
@@ -27,12 +25,29 @@  void test_1a (void *q, void *r)
   /* { dg-message "argument 1 \\('p'\\) NULL where non-null expected" "note" { target *-*-* } .-1 } */
 }
 
-void test_2 (void *p, void *q, void *r)
+void test_1b (void *p, void *r)
+{
+  foo(p, NULL, r);
+}
+
+void test_1c (void *p, void *q, void *r)
+{
+  foo(p, q, NULL); /* { dg-warning "use of NULL where non-null expected" } */
+}
+
+void test_2a (void *p, void *q, void *r)
 {
   bar(p, q, r);
-  bar(NULL, q, r); /* { dg-warning "use of NULL where non-null expected" "warning" } */
+}
+
+void test_2b (void *p, void *q, void *r)
+{
   bar(p, NULL, r); /* { dg-warning "use of NULL where non-null expected" "warning" } */
   /* { dg-message "argument 2 NULL where non-null expected" "note" { target *-*-* } .-1 } */
+}
+
+void test_2c (void *p, void *q, void *r)
+{
   bar(p, q, NULL); /* { dg-warning "use of NULL where non-null expected" "warning" } */
 }
 
diff --git a/gcc/testsuite/gcc.dg/analyzer/call-summaries-2.c b/gcc/testsuite/gcc.dg/analyzer/call-summaries-2.c
index 2d82d02e4e2..1bac24f4c67 100644
--- a/gcc/testsuite/gcc.dg/analyzer/call-summaries-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/call-summaries-2.c
@@ -607,17 +607,22 @@  void partially_inits (int *p, int v)
   p[1] = v;
 }
 
-void test_partially_inits (int x)
+void test_partially_inits_0 (int x)
 {
   int arr[2];
   partially_inits (arr, x);
   partially_inits (arr, x);
 
-  __analyzer_eval (arr[0]); /* { dg-warning "UNKNOWN" "eval" } */
-  /* { dg-warning "use of uninitialized value 'arr\\\[0\\\]'" "uninit" { target *-*-* } .-1 } */
+  __analyzer_eval (arr[0]); /* { dg-warning "use of uninitialized value 'arr\\\[0\\\]'" } */
+}
+
+void test_partially_inits_1 (int x)
+{
+  int arr[2];
+  partially_inits (arr, x);
+  partially_inits (arr, x);
 
-  __analyzer_eval (arr[1] == x); /* { dg-warning "UNKNOWN" "eval" } */ 
-  /* { dg-bogus "use of uninitialized value 'arr\\\[1\\\]'" "uninit" { xfail *-*-* } .-1 } */
+  __analyzer_eval (arr[1] == x); /* { dg-bogus "use of uninitialized value 'arr\\\[1\\\]'" "uninit" { xfail *-*-* } } */
   // TODO(xfail), and eval should be "TRUE"
 }
 
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
index d8930d189bb..86d1ccf11a7 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
@@ -351,9 +351,8 @@  void test_19 (void)
 {
   int i, j; /* { dg-message "region created on stack here" } */
   /* Compare two uninitialized locals.  */
-    __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" "unknown " } */
-    /* { dg-warning "use of uninitialized value 'i'" "uninit i" { target *-*-* } .-1 } */
-    /* { dg-warning "use of uninitialized value 'j'" "uninit j" { target *-*-* } .-2 } */
+    __analyzer_eval (i == j); /* { dg-warning "use of uninitialized value 'i'" "uninit i" } */
+    /* { dg-warning "use of uninitialized value 'j'" "uninit j" { target *-*-* } .-1 } */
 }
 
 void test_20 (int i, int j)
@@ -653,11 +652,6 @@  void test_29b (void)
   __analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */
   __analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */
 
-  __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" "unknown" } */
-  /* { dg-warning "use of uninitialized value 'p\\\[10\\\].x'" "uninit" { target *-*-* } .-1 } */
-  __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" "unknown" } */
-  /* { dg-warning "use of uninitialized value 'p\\\[10\\\].y'" "uninit" { target *-*-* } .-1 } */
-
   q = &p[7];
 
   __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
@@ -679,6 +673,8 @@  void test_29b (void)
 
   __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
   __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[10].x == 0); /* { dg-warning "use of uninitialized value 'p\\\[10\\\].x'" } */
 }
 
 void test_29c (int len)
@@ -704,11 +700,6 @@  void test_29c (int len)
   __analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */
   __analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */
 
-  __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" "unknown" } */
-  /* { dg-warning "use of uninitialized value '\\*p\\\[10\\\].x'" "uninit" { target *-*-* } .-1 } */
-  __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" "unknown" } */
-  /* { dg-warning "use of uninitialized value '\\*p\\\[10\\\].y'" "uninit" { target *-*-* } .-1 } */
-
   q = &p[7];
 
   __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
@@ -730,6 +721,8 @@  void test_29c (int len)
 
   __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
   __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[10].x == 0); /* { dg-warning "use of uninitialized value '\\*p\\\[10\\\].x'" } */
 }
 
 void test_30 (void *ptr)
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5.c
index 2135c70eb8d..b71bad757a1 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-5.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5.c
@@ -90,10 +90,6 @@  void unref (base_obj *obj)
 {
   if (--obj->ob_refcnt == 0) /* { dg-bogus "dereference of uninitialized pointer 'obj'" } */
     obj->ob_type->tp_dealloc (obj);
-  /* { dg-warning "dereference of NULL 'obj'" "deref of NULL" { target *-*-* } .-2 } */
-  /* FIXME: ideally we wouldn't issue this, as we've already issued a
-     warning about str_obj which is now in the "stop" state; the cast
-     confuses things.  */
 }
 
 void test_1 (const char *str)
diff --git a/gcc/testsuite/gcc.dg/analyzer/doom-s_sound-pr108867.c b/gcc/testsuite/gcc.dg/analyzer/doom-s_sound-pr108867.c
new file mode 100644
index 00000000000..ebbfed2982a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/doom-s_sound-pr108867.c
@@ -0,0 +1,653 @@ 
+/* Reduced from Doom's linuxdoom-1.10/s_sound.c, which is GPLv2 or later.  */
+
+/* { dg-additional-options "-fno-analyzer-call-summaries -Wno-analyzer-too-complex" } */
+
+typedef struct _IO_FILE FILE;
+extern FILE* stderr;
+extern int
+fprintf(FILE* __restrict __stream, const char* __restrict __format, ...);
+extern int
+sprintf(char* __restrict __s, const char* __restrict __format, ...)
+  __attribute__((__nothrow__));
+extern int
+abs(int __x) __attribute__((__nothrow__, __leaf__)) __attribute__((__const__));
+
+typedef enum
+{
+  false,
+  true
+} boolean;
+
+typedef unsigned char byte;
+
+void
+I_Error(char* error, ...);
+
+typedef enum
+{
+  shareware,
+  registered,
+  commercial,
+  /* [...snip...] */
+} GameMode_t;
+
+typedef int fixed_t;
+
+fixed_t
+FixedMul(fixed_t a, fixed_t b);
+
+extern fixed_t finesine[5 * 8192 / 4];
+typedef unsigned angle_t;
+
+typedef struct mobj_s
+{
+  /* [...snip...] */
+  fixed_t x;
+  fixed_t y;
+  fixed_t z;
+  /* [...snip...] */
+  angle_t angle;
+  /* [...snip...] */
+} mobj_t;
+
+typedef struct player_s
+{
+  mobj_t* mo;
+  /* [...snip...] */
+} player_t;
+
+extern GameMode_t gamemode;
+extern int gameepisode;
+extern int gamemap;
+extern int consoleplayer;
+extern player_t players[4];
+
+typedef struct sfxinfo_struct sfxinfo_t;
+
+struct sfxinfo_struct
+{
+  /* [...snip...] */
+  int priority;
+  sfxinfo_t* link;
+  int pitch;
+  int volume;
+  void* data;
+  int usefulness;
+  int lumpnum;
+};
+
+typedef struct
+{
+  char* name;
+  int lumpnum;
+  void* data;
+  int handle;
+} musicinfo_t;
+
+extern sfxinfo_t S_sfx[];
+
+extern musicinfo_t S_music[];
+
+typedef enum
+{
+  mus_None,
+  mus_e1m1,
+  /* [...snip...] */
+  mus_e1m5,
+  /* [...snip...] */
+  mus_e1m9,
+  /* [...snip...] */
+  mus_e2m4,
+  mus_e2m5,
+  mus_e2m6,
+  mus_e2m7,
+  /* [...snip...] */
+  mus_e3m2,
+  mus_e3m3,
+  mus_e3m4,
+  /* [...snip...] */
+  mus_runnin,
+  /* [...snip...] */
+  NUMMUSIC
+} musicenum_t;
+
+typedef enum
+{
+  /* [...snip...] */
+  sfx_sawup,
+  /* [...snip...] */
+  sfx_sawhit,
+  /* [...snip...] */
+  sfx_itemup,
+  /* [...snip...] */
+  sfx_tink,
+  /* [...snip...] */
+  NUMSFX
+} sfxenum_t;
+
+
+void
+I_SetChannels();
+
+int
+I_GetSfxLumpNum(sfxinfo_t* sfxinfo);
+
+int
+I_StartSound(int id, int vol, int sep, int pitch, int priority);
+
+void
+I_StopSound(int handle);
+int
+I_SoundIsPlaying(int handle);
+void
+I_UpdateSoundParams(int handle, int vol, int sep, int pitch);
+
+void
+I_SetMusicVolume(int volume);
+
+void
+I_PauseSong(int handle);
+void
+I_ResumeSong(int handle);
+int
+I_RegisterSong(void* data);
+
+void
+I_PlaySong(int handle, int looping);
+
+void
+I_StopSong(int handle);
+
+void
+I_UnRegisterSong(int handle);
+void
+S_StopSound(void* origin);
+void
+S_ChangeMusic(int music_id, int looping);
+void
+S_StopMusic(void);
+
+void
+S_SetMusicVolume(int volume);
+void
+S_SetSfxVolume(int volume);
+
+void*
+Z_Malloc(int size, int tag, void* ptr);
+void
+Z_ChangeTag2(void* ptr, int tag);
+
+typedef struct memblock_s
+{
+  /* [...snip...] */
+  int id;
+  /* [...snip...] */
+} memblock_t;
+int
+M_Random(void);
+int
+W_GetNumForName(char* name);
+void*
+W_CacheLumpNum(int lump, int tag);
+angle_t
+R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2);
+
+typedef struct
+{
+  sfxinfo_t* sfxinfo;
+  void* origin;
+  int handle;
+} channel_t;
+static channel_t* channels;
+
+int snd_SfxVolume = 15;
+int snd_MusicVolume = 15;
+static boolean mus_paused;
+static musicinfo_t* mus_playing = 0;
+int numChannels;
+static int nextcleanup;
+
+int
+S_getChannel(void* origin, sfxinfo_t* sfxinfo);
+
+int
+S_AdjustSoundParams(mobj_t* listener,
+                    mobj_t* source,
+                    int* vol,
+                    int* sep,
+                    int* pitch);
+void
+S_StopChannel(int cnum);
+
+void
+S_Init(int sfxVolume, int musicVolume)
+{
+  int i;
+
+  fprintf(stderr, "S_Init: default sfx volume %d\n", sfxVolume);
+
+  I_SetChannels();
+
+  S_SetSfxVolume(sfxVolume);
+
+  S_SetMusicVolume(musicVolume);
+
+  channels = (channel_t*)Z_Malloc(numChannels * sizeof(channel_t), 1, 0);
+
+  for (i = 0; i < numChannels; i++)
+    channels[i].sfxinfo = 0;
+
+  mus_paused = 0;
+
+  for (i = 1; i < NUMSFX; i++)
+    S_sfx[i].lumpnum = S_sfx[i].usefulness = -1;
+}
+void
+S_Start(void)
+{
+  int cnum;
+  int mnum;
+
+  for (cnum = 0; cnum < numChannels; cnum++)
+    if (channels[cnum].sfxinfo)
+      S_StopChannel(cnum);
+
+  mus_paused = 0;
+
+  if (gamemode == commercial)
+    mnum = mus_runnin + gamemap - 1;
+  else {
+    int spmus[] = {
+
+      mus_e3m4, mus_e3m2, mus_e3m3, mus_e1m5, mus_e2m7,
+      mus_e2m4, mus_e2m6, mus_e2m5, mus_e1m9
+    };
+
+    if (gameepisode < 4)
+      mnum = mus_e1m1 + (gameepisode - 1) * 9 + gamemap - 1;
+    else
+      mnum = spmus[gamemap - 1];
+  }
+
+  S_ChangeMusic(mnum, true);
+
+  nextcleanup = 15;
+}
+
+void
+S_StartSoundAtVolume(void* origin_p, int sfx_id, int volume)
+{
+
+  int rc;
+  int sep;
+  int pitch;
+  int priority;
+  sfxinfo_t* sfx;
+  int cnum;
+
+  mobj_t* origin = (mobj_t*)origin_p;
+  if (sfx_id < 1 || sfx_id > NUMSFX)
+    I_Error("Bad sfx #: %d", sfx_id);
+
+  sfx = &S_sfx[sfx_id];
+
+  if (sfx->link) {
+    pitch = sfx->pitch;
+    priority = sfx->priority;
+    volume += sfx->volume;
+
+    if (volume < 1)
+      return;
+
+    if (volume > snd_SfxVolume)
+      volume = snd_SfxVolume;
+  } else {
+    pitch = 128;
+    priority = 64;
+  }
+
+  if (origin && origin != players[consoleplayer].mo) {
+    rc = S_AdjustSoundParams(
+      players[consoleplayer].mo, origin, &volume, &sep, &pitch);
+
+    if (origin->x == players[consoleplayer].mo->x &&
+        origin->y == players[consoleplayer].mo->y) {
+      sep = 128;
+    }
+
+    if (!rc)
+      return;
+  } else {
+    sep = 128;
+  }
+
+  if (sfx_id >= sfx_sawup && sfx_id <= sfx_sawhit) {
+    pitch += 8 - (M_Random() & 15);
+
+    if (pitch < 0)
+      pitch = 0;
+    else if (pitch > 255)
+      pitch = 255;
+  } else if (sfx_id != sfx_itemup && sfx_id != sfx_tink) {
+    pitch += 16 - (M_Random() & 31);
+
+    if (pitch < 0)
+      pitch = 0;
+    else if (pitch > 255)
+      pitch = 255;
+  }
+
+  S_StopSound(origin);
+
+  cnum = S_getChannel(origin, sfx);
+
+  if (cnum < 0)
+    return;
+  if (sfx->lumpnum < 0)
+    sfx->lumpnum = I_GetSfxLumpNum(sfx);
+
+  if (!sfx->data) {
+    fprintf(stderr, "S_StartSoundAtVolume: 16bit and not pre-cached - wtf?\n");
+  }
+
+  if (sfx->usefulness++ < 0)
+    sfx->usefulness = 1;
+
+  channels[cnum].handle = I_StartSound(sfx_id,
+
+                                       volume,
+                                       sep,
+                                       pitch,
+                                       priority);
+}
+
+void
+S_StartSound(void* origin, int sfx_id)
+{
+
+  S_StartSoundAtVolume(origin, sfx_id, snd_SfxVolume);
+}
+
+void
+S_StopSound(void* origin)
+{
+
+  int cnum;
+
+  for (cnum = 0; cnum < numChannels; cnum++) {
+    if (channels[cnum].sfxinfo && channels[cnum].origin == origin) {
+      S_StopChannel(cnum);
+      break;
+    }
+  }
+}
+void
+S_PauseSound(void)
+{
+  if (mus_playing && !mus_paused) {
+    I_PauseSong(mus_playing->handle);
+    mus_paused = true;
+  }
+}
+
+void
+S_ResumeSound(void)
+{
+  if (mus_playing && mus_paused) {
+    I_ResumeSong(mus_playing->handle);
+    mus_paused = false;
+  }
+}
+
+void
+S_UpdateSounds(void* listener_p)
+{
+  int audible;
+  int cnum;
+  int volume;
+  int sep;
+  int pitch;
+  sfxinfo_t* sfx;
+  channel_t* c;
+
+  mobj_t* listener = (mobj_t*)listener_p;
+  for (cnum = 0; cnum < numChannels; cnum++) {
+    c = &channels[cnum];
+    sfx = c->sfxinfo;
+
+    if (c->sfxinfo) {
+      if (I_SoundIsPlaying(c->handle)) {
+
+        volume = snd_SfxVolume;
+        pitch = 128;
+        sep = 128;
+
+        if (sfx->link) {
+          pitch = sfx->pitch;
+          volume += sfx->volume;
+          if (volume < 1) {
+            S_StopChannel(cnum);
+            continue;
+          } else if (volume > snd_SfxVolume) {
+            volume = snd_SfxVolume;
+          }
+        }
+
+        if (c->origin && listener_p != c->origin) {
+          audible =
+            S_AdjustSoundParams(listener, c->origin, &volume, &sep, &pitch);
+
+          if (!audible) {
+            S_StopChannel(cnum);
+          } else
+            I_UpdateSoundParams(c->handle, volume, sep, pitch);
+        }
+      } else {
+
+        S_StopChannel(cnum);
+      }
+    }
+  }
+}
+
+void
+S_SetMusicVolume(int volume)
+{
+  if (volume < 0 || volume > 127) {
+    I_Error("Attempt to set music volume at %d", volume);
+  }
+
+  I_SetMusicVolume(127);
+  I_SetMusicVolume(volume);
+  snd_MusicVolume = volume;
+}
+
+void
+S_SetSfxVolume(int volume)
+{
+
+  if (volume < 0 || volume > 127)
+    I_Error("Attempt to set sfx volume at %d", volume);
+
+  snd_SfxVolume = volume;
+}
+
+void
+S_StartMusic(int m_id)
+{
+  S_ChangeMusic(m_id, false);
+}
+
+void
+S_ChangeMusic(int musicnum, int looping)
+{
+  musicinfo_t* music;
+  char namebuf[9];
+
+  if ((musicnum <= mus_None) || (musicnum >= NUMMUSIC)) {
+    I_Error("Bad music number %d", musicnum);
+  } else
+    music = &S_music[musicnum];
+
+  /* We don't know that I_Error exits, so actually a false positive;
+     see PR analyzer/108867.  */
+
+  if (mus_playing == music) /* { dg-warning "use of uninitialized value 'music'" } */
+    return;
+
+  S_StopMusic();
+
+  /* We shouldn't issue further warnings about 'music' being
+     uninitialized.  */
+
+  if (!music->lumpnum) { /* { dg-bogus "use of uninitialized value 'music'" } */
+    sprintf(namebuf, "d_%s", music->name); /* { dg-bogus "use of uninitialized value 'music'" } */
+    music->lumpnum = W_GetNumForName(namebuf); /* { dg-bogus "use of uninitialized value 'music'" } */
+  }
+
+  music->data = (void*)W_CacheLumpNum(music->lumpnum, 3); /* { dg-bogus "use of uninitialized value 'music'" } */
+  music->handle = I_RegisterSong(music->data); /* { dg-bogus "use of uninitialized value 'music'" } */
+
+  I_PlaySong(music->handle, looping); /* { dg-bogus "use of uninitialized value 'music'" } */
+
+  mus_playing = music; /* { dg-bogus "use of uninitialized value 'music'" } */
+}
+
+void
+S_StopMusic(void)
+{
+  if (mus_playing) {
+    if (mus_paused)
+      I_ResumeSong(mus_playing->handle);
+
+    I_StopSong(mus_playing->handle);
+    I_UnRegisterSong(mus_playing->handle);
+    {
+      if (((memblock_t*)((byte*)(mus_playing->data) - sizeof(memblock_t)))
+            ->id != 0x1d4a11)
+        I_Error("Z_CT at "
+                "s_sound.c"
+                ":%i",
+                699);
+      Z_ChangeTag2(mus_playing->data, 101);
+    };
+    ;
+
+    mus_playing->data = 0;
+    mus_playing = 0;
+  }
+}
+
+void
+S_StopChannel(int cnum)
+{
+
+  int i;
+  channel_t* c = &channels[cnum];
+
+  if (c->sfxinfo) {
+
+    if (I_SoundIsPlaying(c->handle)) {
+
+      I_StopSound(c->handle);
+    }
+
+    for (i = 0; i < numChannels; i++) {
+      if (cnum != i && c->sfxinfo == channels[i].sfxinfo) {
+        break;
+      }
+    }
+
+    c->sfxinfo->usefulness--;
+
+    c->sfxinfo = 0;
+  }
+}
+int
+S_AdjustSoundParams(mobj_t* listener,
+                    mobj_t* source,
+                    int* vol,
+                    int* sep,
+                    int* pitch)
+{
+  fixed_t approx_dist;
+  fixed_t adx;
+  fixed_t ady;
+  angle_t angle;
+
+  adx = abs(listener->x - source->x);
+  ady = abs(listener->y - source->y);
+
+  approx_dist = adx + ady - ((adx < ady ? adx : ady) >> 1);
+
+  if (gamemap != 8 && approx_dist > (1200 * 0x10000)) {
+    return 0;
+  }
+
+  angle = R_PointToAngle2(listener->x, listener->y, source->x, source->y);
+
+  if (angle > listener->angle)
+    angle = angle - listener->angle;
+  else
+    angle = angle + (0xffffffff - listener->angle);
+
+  angle >>= 19;
+
+  *sep = 128 - (FixedMul((96 * 0x10000), finesine[angle]) >> 16);
+
+  if (approx_dist < (160 * 0x10000)) {
+    *vol = snd_SfxVolume;
+  } else if (gamemap == 8) {
+    if (approx_dist > (1200 * 0x10000))
+      approx_dist = (1200 * 0x10000);
+
+    *vol =
+      15 + ((snd_SfxVolume - 15) * (((1200 * 0x10000) - approx_dist) >> 16)) /
+             (((1200 * 0x10000) - (160 * 0x10000)) >> 16);
+  } else {
+
+    *vol = (snd_SfxVolume * (((1200 * 0x10000) - approx_dist) >> 16)) /
+           (((1200 * 0x10000) - (160 * 0x10000)) >> 16);
+  }
+
+  return (*vol > 0);
+}
+int
+S_getChannel(void* origin, sfxinfo_t* sfxinfo)
+{
+
+  int cnum;
+
+  channel_t* c;
+
+  for (cnum = 0; cnum < numChannels; cnum++) {
+    if (!channels[cnum].sfxinfo)
+      break;
+    else if (origin && channels[cnum].origin == origin) {
+      S_StopChannel(cnum);
+      break;
+    }
+  }
+
+  if (cnum == numChannels) {
+
+    for (cnum = 0; cnum < numChannels; cnum++)
+      if (channels[cnum].sfxinfo->priority >= sfxinfo->priority) /* { dg-warning "dereference of NULL" } */
+        break;
+
+    if (cnum == numChannels) {
+
+      return -1;
+    } else {
+
+      S_StopChannel(cnum);
+    }
+  }
+
+  c = &channels[cnum];
+
+  c->sfxinfo = sfxinfo;
+  c->origin = origin;
+
+  return cnum;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/memset-CVE-2017-18549-1.c b/gcc/testsuite/gcc.dg/analyzer/memset-CVE-2017-18549-1.c
index b12408a69a0..418168d413e 100644
--- a/gcc/testsuite/gcc.dg/analyzer/memset-CVE-2017-18549-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/memset-CVE-2017-18549-1.c
@@ -5,6 +5,8 @@ 
    It was fixed by e.g. 342ffc26693b528648bdc9377e51e4f2450b4860 on linux-4.13.y
    in linux-stable.  */
 
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include "analyzer-decls.h"
 #include <string.h>
 
diff --git a/gcc/testsuite/gcc.dg/analyzer/null-deref-pr108830.c b/gcc/testsuite/gcc.dg/analyzer/null-deref-pr108830.c
new file mode 100644
index 00000000000..417ab00794d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/null-deref-pr108830.c
@@ -0,0 +1,94 @@ 
+/* Reduced from apr-1.7.0/tables/apr_hash.c: 'apr_hash_merge' */
+
+/* { dg-additional-options "-Wno-analyzer-too-complex" } */
+
+#define NULL ((void*)0)
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void*
+memset(void* __s, int __c, size_t __n)
+  __attribute__((__nothrow__, __leaf__, __nonnull__(1)));
+
+typedef struct apr_pool_t apr_pool_t;
+
+void*
+apr_palloc(apr_pool_t* p, size_t size)
+  __attribute__((alloc_size(2), nonnull(1)));
+
+typedef struct apr_hash_t apr_hash_t;
+typedef struct apr_hash_index_t apr_hash_index_t;
+typedef unsigned int (*apr_hashfunc_t)(const char* key, size_t* klen);
+typedef struct apr_hash_entry_t apr_hash_entry_t;
+
+struct apr_hash_entry_t
+{
+  apr_hash_entry_t* next;
+  unsigned int hash;
+  const void* key;
+  size_t klen;
+  const void* val;
+};
+
+struct apr_hash_t
+{
+  apr_pool_t* pool;
+  apr_hash_entry_t** array;
+  /* [...snip.../ */
+  unsigned int count, max, seed;
+  apr_hashfunc_t hash_func;
+  apr_hash_entry_t* free;
+};
+
+static apr_hash_entry_t**
+alloc_array(apr_hash_t* ht, unsigned int max)
+{
+  return memset(apr_palloc(ht->pool, sizeof(*ht->array) * (max + 1)),
+                0,
+                sizeof(*ht->array) * (max + 1));
+}
+
+apr_hash_t*
+apr_hash_merge(apr_pool_t* p,
+               const apr_hash_t* overlay,
+               const apr_hash_t* base)
+{
+  apr_hash_t* res;
+  apr_hash_entry_t* new_vals = NULL;
+  apr_hash_entry_t* iter;
+  unsigned int i, j, k;
+  res = apr_palloc(p, sizeof(apr_hash_t));
+  res->pool = p;
+  res->free = NULL;
+  res->hash_func = base->hash_func;
+  res->count = base->count;
+  res->max = (overlay->max > base->max) ? overlay->max : base->max;
+  if (base->count + overlay->count > res->max) {
+    res->max = res->max * 2 + 1;
+  }
+  res->seed = base->seed;
+  res->array = alloc_array(res, res->max);
+  if (base->count + overlay->count) {
+    new_vals =
+      apr_palloc(p, sizeof(apr_hash_entry_t) * (base->count + overlay->count));
+  }
+  j = 0;
+  for (k = 0; k <= base->max; k++) {
+    for (iter = base->array[k]; iter; iter = iter->next) {
+      i = iter->hash & res->max;
+      /* We should only warn for the first of these
+	 (it's actually a false positive, but we don't have the
+	 invariante to know that).  */
+      new_vals[j].klen = iter->klen;   /* { dg-warning "dereference of NULL 'new_vals'" } */
+      /* ...but not for subsequent ones: */
+      new_vals[j].key = iter->key;      /* { dg-bogus "dereference of NULL 'new_vals'" "PR analyzer/108830" } */
+      new_vals[j].val = iter->val;      /* { dg-bogus "dereference of NULL 'new_vals'" "PR analyzer/108830" } */
+      new_vals[j].hash = iter->hash;    /* { dg-bogus "dereference of NULL 'new_vals'" "PR analyzer/108830" } */
+      new_vals[j].next = res->array[i]; /* { dg-bogus "dereference of NULL 'new_vals'" "PR analyzer/108830" } */
+      res->array[i] = &new_vals[j];     /* { dg-bogus "dereference of NULL 'new_vals'" "PR analyzer/108830" } */
+      j++;
+    }
+  }
+  /* [...snip...] */
+  return res;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pipe-1.c b/gcc/testsuite/gcc.dg/analyzer/pipe-1.c
index 6b95442e322..156f0114fd5 100644
--- a/gcc/testsuite/gcc.dg/analyzer/pipe-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/pipe-1.c
@@ -1,3 +1,5 @@ 
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include "analyzer-decls.h"
 
 extern int pipe(int pipefd[2]);
diff --git a/gcc/testsuite/gcc.dg/analyzer/pipe-void-return.c b/gcc/testsuite/gcc.dg/analyzer/pipe-void-return.c
index 0de676305f6..8b1822d97f9 100644
--- a/gcc/testsuite/gcc.dg/analyzer/pipe-void-return.c
+++ b/gcc/testsuite/gcc.dg/analyzer/pipe-void-return.c
@@ -1,3 +1,5 @@ 
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 extern void pipe(int pipefd[2]);
 extern int close(int fd);
 
diff --git a/gcc/testsuite/gcc.dg/analyzer/pipe2-1.c b/gcc/testsuite/gcc.dg/analyzer/pipe2-1.c
index d7afc9caaf9..aa0684e5c86 100644
--- a/gcc/testsuite/gcc.dg/analyzer/pipe2-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/pipe2-1.c
@@ -1,3 +1,5 @@ 
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include "analyzer-decls.h"
 
 extern int pipe2(int pipefd[2], int flags);
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr101547.c b/gcc/testsuite/gcc.dg/analyzer/pr101547.c
index b42e64ce338..d1c3c915e0e 100644
--- a/gcc/testsuite/gcc.dg/analyzer/pr101547.c
+++ b/gcc/testsuite/gcc.dg/analyzer/pr101547.c
@@ -2,10 +2,18 @@  char *
 fopen (const char *restrict, const char *restrict);
 
 void
-k2 (void)
+k2_uninit (void)
 {
   char *setfiles[1];
   int i; /* { dg-message "region created on stack here" } */
 
   setfiles[i] = fopen ("", ""); /* { dg-warning "use of uninitialized value 'i'" } */
+}
+
+void
+k2_leak (int i)
+{
+  char *setfiles[1];
+
+  setfiles[i] = fopen ("", "");
 } /* { dg-warning "leak of FILE" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr101875.c b/gcc/testsuite/gcc.dg/analyzer/pr101875.c
index 7700c7d030f..4bef2f161a4 100644
--- a/gcc/testsuite/gcc.dg/analyzer/pr101875.c
+++ b/gcc/testsuite/gcc.dg/analyzer/pr101875.c
@@ -5,12 +5,22 @@  void
 err (void);
 
 void
-k2 (void)
+k2_uninit (void)
 {
   char *setfiles[1];
   int i; /* { dg-message "region created on stack here" } */
 
   setfiles[i] = fopen("", ""); /* { dg-warning "use of uninitialized value 'i'" } */
-  if (!setfiles[i]) /* { dg-warning "use of uninitialized value 'i'" } */
+  if (!setfiles[i])
+    err ();
+}
+
+void
+k2_leak (int i)
+{
+  char *setfiles[1];
+
+  setfiles[i] = fopen("", "");
+  if (!setfiles[i])
     err ();
 } /* { dg-warning "leak of FILE" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr104224-split.c b/gcc/testsuite/gcc.dg/analyzer/pr104224-split.c
new file mode 100644
index 00000000000..b90fe559f2c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr104224-split.c
@@ -0,0 +1,136 @@ 
+#include <stdio.h>
+
+struct test {
+        int one;
+        int two;
+};
+
+void func2(const struct test *t)
+{
+        if (t->one == 0)
+                printf("init func2\n");
+
+        if (t->two == 0)  /* { dg-warning "uninitialized" } */
+                printf("uninit func2\n");
+}
+
+void func1(struct test *t)
+{
+        t->one = 1;
+        func2(t);
+}
+
+int func3(int num)
+{
+        if (num)
+                return num;
+        else
+                return 0;
+}
+
+void func4(int *a, int max)
+{
+        int i;
+        // skip the first
+        for (i=1; i<max; i++)
+                a[i] = 0;
+}
+
+void func5(const int *a, int max)
+{
+        /* a[0] is uninitialized, but the rest of the array is initialized.  */
+        int i;
+        for (i=0; i<max; i++) {
+                if (a[i]) /* { dg-warning "uninitialized" "" { xfail *-*-* } } */
+                        printf("func5: %d\n", i);
+        }
+}
+
+int func6(const int *num)
+{
+        if (*num)  /* { dg-warning "uninitialized" } */
+                return *num;
+        else
+                return 0;
+}
+
+int j;
+int func7(void)
+{
+        return j;  /* { dg-bogus "uninitialized" } */
+}
+
+void func8(const int *a, int max)
+{
+        int i;
+        for (i=0; i<max; i++) {
+                if (a[i]) /* { dg-warning "uninitialized" } */
+                        printf("func8: %d\n", i);
+        }
+}
+
+enum {RED, AMBER, GREEN, BLACK};
+
+int test_1 (void)
+{
+        struct test t;  /* { dg-message "region created on stack here" } */
+
+        func1(&t);
+        return 0;
+}
+
+int test_2 (void)
+{
+        int num;  /* { dg-message "region created on stack here" } */
+
+        func3(num);  /* { dg-warning "use of uninitialized value 'num'" } */
+        return 0;
+}
+
+int test_3 (void)
+{
+        int arry[10];
+
+        func4(arry, 10);
+        func5(arry, 10);
+
+        return 0;
+}
+
+int test_4 (void)
+{
+        int num;  /* { dg-message "region created on stack here" } */
+
+        func6(&num);
+        return 0;
+}
+
+int test_5 (void)
+{
+        int arry_2[10];  /* { dg-message "region created on stack here" } */
+
+        printf("func7: %d\n", func7());
+        func8(arry_2, 10);
+
+        return 0;
+}
+
+int test_6 (void)
+{
+        int go;  /* { dg-message "region created on stack here" } */
+        int color = BLACK;
+
+        switch (color) {
+        case RED:
+        case AMBER:
+                go = 0;
+                break;
+        case GREEN:
+                go = 1;
+                break;
+        }
+
+        printf("go :%d\n", go); /* { dg-warning "use of uninitialized value 'go'" } */
+
+        return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr104224.c b/gcc/testsuite/gcc.dg/analyzer/pr104224.c
index b047c4ca5f2..1ff5f9eae71 100644
--- a/gcc/testsuite/gcc.dg/analyzer/pr104224.c
+++ b/gcc/testsuite/gcc.dg/analyzer/pr104224.c
@@ -1,3 +1,5 @@ 
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include <stdio.h>
 
 struct test {
diff --git a/gcc/testsuite/gcc.dg/analyzer/realloc-2.c b/gcc/testsuite/gcc.dg/analyzer/realloc-2.c
index a39775354a3..ab3e4b6628a 100644
--- a/gcc/testsuite/gcc.dg/analyzer/realloc-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/realloc-2.c
@@ -1,3 +1,5 @@ 
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include "analyzer-decls.h"
 
 typedef __SIZE_TYPE__ size_t;
diff --git a/gcc/testsuite/gcc.dg/analyzer/realloc-3.c b/gcc/testsuite/gcc.dg/analyzer/realloc-3.c
index 89676e1ca0d..eec61497d5b 100644
--- a/gcc/testsuite/gcc.dg/analyzer/realloc-3.c
+++ b/gcc/testsuite/gcc.dg/analyzer/realloc-3.c
@@ -1,3 +1,5 @@ 
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include "analyzer-decls.h"
 
 typedef __SIZE_TYPE__ size_t;
diff --git a/gcc/testsuite/gcc.dg/analyzer/realloc-5.c b/gcc/testsuite/gcc.dg/analyzer/realloc-5.c
index 75f0b70a996..137e05b87aa 100644
--- a/gcc/testsuite/gcc.dg/analyzer/realloc-5.c
+++ b/gcc/testsuite/gcc.dg/analyzer/realloc-5.c
@@ -1,3 +1,5 @@ 
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include "analyzer-decls.h"
 
 typedef __SIZE_TYPE__ size_t;
diff --git a/gcc/testsuite/gcc.dg/analyzer/stdarg-1-ms_abi.c b/gcc/testsuite/gcc.dg/analyzer/stdarg-1-ms_abi.c
index e55f10de66b..4422075e2b5 100644
--- a/gcc/testsuite/gcc.dg/analyzer/stdarg-1-ms_abi.c
+++ b/gcc/testsuite/gcc.dg/analyzer/stdarg-1-ms_abi.c
@@ -1,5 +1,6 @@ 
 /* As per stdarg-1.c, but using the ms_abi versions of the builtins.  */
 
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
 /* { dg-do compile { target { x86_64-*-* && lp64 } } } */
 
 #include "analyzer-decls.h"
diff --git a/gcc/testsuite/gcc.dg/analyzer/stdarg-1-sysv_abi.c b/gcc/testsuite/gcc.dg/analyzer/stdarg-1-sysv_abi.c
index fb49b3584e2..ba90df20588 100644
--- a/gcc/testsuite/gcc.dg/analyzer/stdarg-1-sysv_abi.c
+++ b/gcc/testsuite/gcc.dg/analyzer/stdarg-1-sysv_abi.c
@@ -1,5 +1,6 @@ 
 /* As per stdarg-1.c, but using the sysv_abi versions of the builtins.  */
 
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
 /* { dg-do compile { target { x86_64-*-* && lp64 } } } */
 
 #include "analyzer-decls.h"
diff --git a/gcc/testsuite/gcc.dg/analyzer/stdarg-1.c b/gcc/testsuite/gcc.dg/analyzer/stdarg-1.c
index f2766a50522..4d5431b822c 100644
--- a/gcc/testsuite/gcc.dg/analyzer/stdarg-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/stdarg-1.c
@@ -1,3 +1,5 @@ 
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include "analyzer-decls.h"
 
 /* Unpacking a va_list.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/symbolic-1.c b/gcc/testsuite/gcc.dg/analyzer/symbolic-1.c
index 2f4e00bcc25..0d78242381a 100644
--- a/gcc/testsuite/gcc.dg/analyzer/symbolic-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/symbolic-1.c
@@ -1,3 +1,5 @@ 
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include "analyzer-decls.h"
 
 /* The example from store.h  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/symbolic-7.c b/gcc/testsuite/gcc.dg/analyzer/symbolic-7.c
index 665e0b645d3..c1464555e8a 100644
--- a/gcc/testsuite/gcc.dg/analyzer/symbolic-7.c
+++ b/gcc/testsuite/gcc.dg/analyzer/symbolic-7.c
@@ -31,16 +31,20 @@  void test_2 (int i)
   __analyzer_eval (arr[i] == 42); /* { dg-warning "UNKNOWN" } */
 }
 
-void test_3 (int i)
+void test_3_concrete_read (int i)
 {
   /* An array that can't have been touched.  */
   int arr[2];
   
   /* Concrete reads.  */
-  __analyzer_eval (arr[0] == 42); /* { dg-warning "UNKNOWN" "unknown" } */
-  /* { dg-warning "use of uninitialized value 'arr\\\[0\\\]'" "uninit" { target *-*-* } .-1 } */
+  __analyzer_eval (arr[0] == 42); /* { dg-warning "use of uninitialized value 'arr\\\[0\\\]'" } */
+}
 
+void test_3_symbolic_read (int i)
+{
+  /* An array that can't have been touched.  */
+  int arr[2];
+  
   /* Symbolic read.  */
-  __analyzer_eval (arr[i] == 42); /* { dg-warning "UNKNOWN" "unknown" } */
-  /* { dg-warning "use of uninitialized value 'arr\\\[i\\\]'" "uninit" { target *-*-* } .-1 } */
+  __analyzer_eval (arr[i] == 42); /* { dg-warning "use of uninitialized value 'arr\\\[i\\\]'" } */
 }
diff --git a/gcc/testsuite/gcc.dg/analyzer/uninit-4.c b/gcc/testsuite/gcc.dg/analyzer/uninit-4.c
index 616cb342348..31c6ae0c611 100644
--- a/gcc/testsuite/gcc.dg/analyzer/uninit-4.c
+++ b/gcc/testsuite/gcc.dg/analyzer/uninit-4.c
@@ -22,7 +22,7 @@  alloc_foo (int a, int b)
   return p;
 }
 
-void test (int x, int y, int z)
+void test_access_inited_fields (int x, int y, int z)
 {
   struct foo *p = alloc_foo (x, z);
   if (!p)
@@ -30,10 +30,20 @@  void test (int x, int y, int z)
 
   __analyzer_eval (p->i == x); /* { dg-warning "TRUE" } */
 
-  __analyzer_eval (p->j == y); /* { dg-warning "UNKNOWN" "unknown" } */
-  /* { dg-warning "use of uninitialized value '\\*p\\.j'" "uninit" { target *-*-* } .-1 } */
-
   __analyzer_eval (p->k == z); /* { dg-warning "TRUE" } */
   
   free (p);
 }
+
+void test_stop_after_accessing_uninit (int x, int y, int z)
+{
+  struct foo *p = alloc_foo (x, z);
+  if (!p)
+    return;
+
+  __analyzer_eval (p->i == x); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p->j == y); /* { dg-warning "use of uninitialized value '\\*p\\.j'" } */
+
+  __analyzer_dump_path (); /* { dg-bogus "path" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/uninit-8.c b/gcc/testsuite/gcc.dg/analyzer/uninit-8.c
new file mode 100644
index 00000000000..2fa10525cd0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/uninit-8.c
@@ -0,0 +1,73 @@ 
+struct st
+{
+  int a, b, c, d, e;
+};
+
+int
+test_1 (int flag, struct st *p)
+{
+  struct st *q;
+  int result = 0;
+  if (flag)
+    q = p;
+  /* We should only warn about the first use of uninit for 'q':  */
+  result += q->a; /* { dg-warning "use of uninitialized value 'q'" } */
+  /* ...and not for these:  */
+  result += q->b; /* { dg-bogus "use of uninitialized value 'q'" } */
+  result += q->c; /* { dg-bogus "use of uninitialized value 'q'" } */
+  result += q->d; /* { dg-bogus "use of uninitialized value 'q'" } */
+  result += q->e; /* { dg-bogus "use of uninitialized value 'q'" } */
+  return result;
+}
+
+int
+test_2 (int flag, struct st *p, struct st *r)
+{
+  struct st *q;
+  int result = 0;
+  if (flag)
+    q = p;
+  /* We should only warn about the first use of uninit for 'q':  */
+  if (q == r) /* { dg-warning "use of uninitialized value 'q'" } */
+    result += 1;
+  /* ...and not for these, after a conditional:  */
+  result += q->b; /* { dg-bogus "use of uninitialized value 'q'" } */
+  result += q->c; /* { dg-bogus "use of uninitialized value 'q'" } */
+  result += q->d; /* { dg-bogus "use of uninitialized value 'q'" } */
+  result += q->e; /* { dg-bogus "use of uninitialized value 'q'" } */
+  return result;
+}
+
+int
+test_3 (int flag, int val)
+{
+  int result = 0;
+  int idx;
+  if (flag)
+    idx = val;
+  switch (idx) /* { dg-warning "use of uninitialized value 'idx'" } */
+    {
+    case 0:
+      result = 3;
+      break;
+    case 1:
+      result = 4;
+      break;
+    default:
+      result = 5;
+      break;      
+    }
+  switch (idx) /* { dg-bogus "use of uninitialized value 'idx'" } */
+    {
+    case 0:
+      result += 3;
+      break;
+    case 1:
+      result += 4;
+      break;
+    default:
+      result += 5;
+      break;
+    }
+  return result;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/uninit-pr94713.c b/gcc/testsuite/gcc.dg/analyzer/uninit-pr94713.c
index e3bb8ceb5b3..cb91462c36b 100644
--- a/gcc/testsuite/gcc.dg/analyzer/uninit-pr94713.c
+++ b/gcc/testsuite/gcc.dg/analyzer/uninit-pr94713.c
@@ -1,11 +1,19 @@ 
 void f1 (int *);
 void f2 (int);
 
-int foo (void)
+int test_1 (void)
 {
   int *p; /* { dg-message "region created on stack here" } */
 
   f1 (p); /* { dg-warning "use of uninitialized value 'p'" } */
+  f1 (p); /* { dg-bogus "use of uninitialized value 'p'" "no followup warnings" } */
+  return 0;
+}
+
+int test_2 (void)
+{
+  int *p; /* { dg-message "region created on stack here" } */
+
   f2 (p[0]); /* { dg-warning "use of uninitialized value 'p'" } */
   return 0;
 }
diff --git a/gcc/testsuite/gcc.dg/analyzer/zlib-6a.c b/gcc/testsuite/gcc.dg/analyzer/zlib-6a.c
index 9676e0b3845..8c83de4863b 100644
--- a/gcc/testsuite/gcc.dg/analyzer/zlib-6a.c
+++ b/gcc/testsuite/gcc.dg/analyzer/zlib-6a.c
@@ -1,3 +1,5 @@ 
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 typedef unsigned char Byte;
 typedef unsigned int uInt;
 typedef unsigned long uLong;