analyzer: stop exploring the path after certain diagnostics [PR108830]
authorDavid Malcolm <dmalcolm@redhat.com>
Tue, 21 Feb 2023 21:58:36 +0000 (16:58 -0500)
committerDavid Malcolm <dmalcolm@redhat.com>
Tue, 21 Feb 2023 21:58:36 +0000 (16:58 -0500)
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)

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>
33 files changed:
gcc/analyzer/analyzer.opt
gcc/analyzer/engine.cc
gcc/analyzer/pending-diagnostic.h
gcc/analyzer/program-state.cc
gcc/analyzer/region-model.cc
gcc/analyzer/sm-malloc.cc
gcc/doc/invoke.texi
gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c
gcc/testsuite/gcc.dg/analyzer/call-summaries-2.c
gcc/testsuite/gcc.dg/analyzer/data-model-1.c
gcc/testsuite/gcc.dg/analyzer/data-model-5.c
gcc/testsuite/gcc.dg/analyzer/doom-s_sound-pr108867.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/memset-CVE-2017-18549-1.c
gcc/testsuite/gcc.dg/analyzer/null-deref-pr108830.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/pipe-1.c
gcc/testsuite/gcc.dg/analyzer/pipe-void-return.c
gcc/testsuite/gcc.dg/analyzer/pipe2-1.c
gcc/testsuite/gcc.dg/analyzer/pr101547.c
gcc/testsuite/gcc.dg/analyzer/pr101875.c
gcc/testsuite/gcc.dg/analyzer/pr104224-split.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/pr104224.c
gcc/testsuite/gcc.dg/analyzer/realloc-2.c
gcc/testsuite/gcc.dg/analyzer/realloc-3.c
gcc/testsuite/gcc.dg/analyzer/realloc-5.c
gcc/testsuite/gcc.dg/analyzer/stdarg-1-ms_abi.c
gcc/testsuite/gcc.dg/analyzer/stdarg-1-sysv_abi.c
gcc/testsuite/gcc.dg/analyzer/stdarg-1.c
gcc/testsuite/gcc.dg/analyzer/symbolic-1.c
gcc/testsuite/gcc.dg/analyzer/symbolic-7.c
gcc/testsuite/gcc.dg/analyzer/uninit-4.c
gcc/testsuite/gcc.dg/analyzer/uninit-8.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/uninit-pr94713.c
gcc/testsuite/gcc.dg/analyzer/zlib-6a.c

index b4dcdb8..9d1a937 100644 (file)
@@ -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.
index 24ded26..a5965c2 100644 (file)
@@ -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,
index d9e9e7f..6423c8b 100644 (file)
@@ -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.  */
index 9a1a8cd..8dade4b 100644 (file)
@@ -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 (),
index e3de74b..f844b51 100644 (file)
@@ -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)
index c24fe73..1ea9b30 100644 (file)
@@ -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.  */
index 078d29d..bcf8510 100644 (file)
@@ -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
index 7c71a71..5700256 100644 (file)
@@ -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" } */
 }
 
index 2d82d02..1bac24f 100644 (file)
@@ -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"
 }
 
index d8930d1..86d1ccf 100644 (file)
@@ -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)
index 2135c70..b71bad7 100644 (file)
@@ -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 (file)
index 0000000..ebbfed2
--- /dev/null
@@ -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;
+}
index b12408a..418168d 100644 (file)
@@ -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 (file)
index 0000000..417ab00
--- /dev/null
@@ -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;
+}
index 6b95442..156f011 100644 (file)
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include "analyzer-decls.h"
 
 extern int pipe(int pipefd[2]);
index 0de6763..8b1822d 100644 (file)
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 extern void pipe(int pipefd[2]);
 extern int close(int fd);
 
index d7afc9c..aa0684e 100644 (file)
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include "analyzer-decls.h"
 
 extern int pipe2(int pipefd[2], int flags);
index b42e64c..d1c3c91 100644 (file)
@@ -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" } */
index 7700c7d..4bef2f1 100644 (file)
@@ -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 (file)
index 0000000..b90fe55
--- /dev/null
@@ -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;
+}
index b047c4c..1ff5f9e 100644 (file)
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include <stdio.h>
 
 struct test {
index a397753..ab3e4b6 100644 (file)
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include "analyzer-decls.h"
 
 typedef __SIZE_TYPE__ size_t;
index 89676e1..eec6149 100644 (file)
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include "analyzer-decls.h"
 
 typedef __SIZE_TYPE__ size_t;
index 75f0b70..137e05b 100644 (file)
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include "analyzer-decls.h"
 
 typedef __SIZE_TYPE__ size_t;
index e55f10d..4422075 100644 (file)
@@ -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"
index fb49b35..ba90df2 100644 (file)
@@ -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"
index f2766a5..4d5431b 100644 (file)
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include "analyzer-decls.h"
 
 /* Unpacking a va_list.  */
index 2f4e00b..0d78242 100644 (file)
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 #include "analyzer-decls.h"
 
 /* The example from store.h  */
index 665e0b6..c146455 100644 (file)
@@ -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\\\]'" } */
 }
index 616cb34..31c6ae0 100644 (file)
@@ -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 (file)
index 0000000..2fa1052
--- /dev/null
@@ -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;
+}
index e3bb8ce..cb91462 100644 (file)
@@ -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;
 }
index 9676e0b..8c83de4 100644 (file)
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
+
 typedef unsigned char Byte;
 typedef unsigned int uInt;
 typedef unsigned long uLong;