analyzer: fix uninit false +ves [PR108664,PR108666,PR108725]
authorDavid Malcolm <dmalcolm@redhat.com>
Wed, 15 Feb 2023 19:52:02 +0000 (14:52 -0500)
committerDavid Malcolm <dmalcolm@redhat.com>
Wed, 15 Feb 2023 19:53:14 +0000 (14:53 -0500)
This patch updates poisoned_value_diagnostic so that, where possible,
it checks to see if the value is still poisoned along the execution
path seen during feasibility analysis, rather than just that seen
in the exploded graph.

Integration testing shows this reduction in the number of
false positives:
  -Wanalyzer-use-of-uninitialized-value: 191 -> 153 (-38)
where the changes happen in:
      coreutils-9.1: 34 -> 20 (-14)
         qemu-7.2.0: 78 -> 54 (-24)

gcc/analyzer/ChangeLog:
PR analyzer/108664
PR analyzer/108666
PR analyzer/108725
* diagnostic-manager.cc (epath_finder::get_best_epath): Add
"target_stmt" param.
(epath_finder::explore_feasible_paths): Likewise.
(epath_finder::process_worklist_item): Likewise.
(saved_diagnostic::calc_best_epath): Pass m_stmt to
epath_finder::get_best_epath.
* engine.cc (feasibility_state::maybe_update_for_edge): Move
per-stmt logic to...
(feasibility_state::update_for_stmt): ...this new function.
* exploded-graph.h (feasibility_state::update_for_stmt): New decl.
* feasible-graph.cc (feasible_node::get_state_at_stmt): New.
* feasible-graph.h: Include "analyzer/exploded-graph.h".
(feasible_node::get_state_at_stmt): New decl.
* infinite-recursion.cc
(infinite_recursion_diagnostic::check_valid_fpath_p): Update for
vfunc signature change.
* pending-diagnostic.h (pending_diagnostic::check_valid_fpath_p):
Convert first param to a reference.  Add stmt param.
* region-model.cc: Include "analyzer/feasible-graph.h".
(poisoned_value_diagnostic::poisoned_value_diagnostic): Add
"check_expr" param.
(poisoned_value_diagnostic::check_valid_fpath_p): New.
(poisoned_value_diagnostic::m_check_expr): New field.
(region_model::check_for_poison): Attempt to supply a check_expr
to the diagnostic
(region_model::deref_rvalue): Add NULL for new check_expr param
of poisoned_value_diagnostic.
(region_model::get_or_create_region_for_heap_alloc): Don't reuse
regions that are marked as TOUCHED.

gcc/testsuite/ChangeLog:
PR analyzer/108664
PR analyzer/108666
PR analyzer/108725
* gcc.dg/analyzer/coreutils-cksum-pr108664.c: New test.
* gcc.dg/analyzer/coreutils-sum-pr108666.c: New test.
* gcc.dg/analyzer/torture/uninit-pr108725.c: New test.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
gcc/analyzer/diagnostic-manager.cc
gcc/analyzer/engine.cc
gcc/analyzer/exploded-graph.h
gcc/analyzer/feasible-graph.cc
gcc/analyzer/feasible-graph.h
gcc/analyzer/infinite-recursion.cc
gcc/analyzer/pending-diagnostic.h
gcc/analyzer/region-model.cc
gcc/testsuite/gcc.dg/analyzer/coreutils-cksum-pr108664.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/coreutils-sum-pr108666.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/torture/uninit-pr108725.c [new file with mode: 0644]

index 4f036a6c28aa4996832a2ae5b198f8a7bfbaa424..0a447f7ba26c2bbf7c43c0a29cb1ef91ad22e196 100644 (file)
@@ -88,6 +88,7 @@ public:
 
   std::unique_ptr<exploded_path>
   get_best_epath (const exploded_node *target_enode,
+                 const gimple *target_stmt,
                  const pending_diagnostic &pd,
                  const char *desc, unsigned diag_idx,
                  std::unique_ptr<feasibility_problem> *out_problem);
@@ -97,6 +98,7 @@ private:
 
   std::unique_ptr<exploded_path>
   explore_feasible_paths (const exploded_node *target_enode,
+                         const gimple *target_stmt,
                          const pending_diagnostic &pd,
                          const char *desc, unsigned diag_idx);
   bool
@@ -104,6 +106,7 @@ private:
                         const trimmed_graph &tg,
                         feasible_graph *fg,
                         const exploded_node *target_enode,
+                        const gimple *target_stmt,
                         const pending_diagnostic &pd,
                         unsigned diag_idx,
                         std::unique_ptr<exploded_path> *out_best_path) const;
@@ -128,6 +131,9 @@ private:
 /* Get the "best" exploded_path for reaching ENODE from the origin,
    returning ownership of it to the caller.
 
+   If TARGET_STMT is non-NULL, then check for reaching that stmt
+   within ENODE.
+
    Ideally we want to report the shortest feasible path.
    Return NULL if we could not find a feasible path
    (when flag_analyzer_feasibility is true).
@@ -141,6 +147,7 @@ private:
 
 std::unique_ptr<exploded_path>
 epath_finder::get_best_epath (const exploded_node *enode,
+                             const gimple *target_stmt,
                              const pending_diagnostic &pd,
                              const char *desc, unsigned diag_idx,
                              std::unique_ptr<feasibility_problem> *out_problem)
@@ -165,7 +172,7 @@ epath_finder::get_best_epath (const exploded_node *enode,
       if (logger)
        logger->log ("trying to find shortest feasible path");
       if (std::unique_ptr<exploded_path> epath
-           = explore_feasible_paths (enode, pd, desc, diag_idx))
+           = explore_feasible_paths (enode, target_stmt, pd, desc, diag_idx))
        {
          if (logger)
            logger->log ("accepting %qs at EN: %i, SN: %i (sd: %i)"
@@ -335,6 +342,9 @@ private:
    TARGET_ENODE by iteratively building a feasible_graph, in which
    every path to a feasible_node is feasible by construction.
 
+   If TARGET_STMT is non-NULL, then check for reaching that stmt
+   within TARGET_ENODE.
+
    We effectively explore the tree of feasible paths in order of shortest
    path until we either find a feasible path to TARGET_ENODE, or hit
    a limit and give up.
@@ -378,6 +388,7 @@ private:
 
 std::unique_ptr<exploded_path>
 epath_finder::explore_feasible_paths (const exploded_node *target_enode,
+                                     const gimple *target_stmt,
                                      const pending_diagnostic &pd,
                                      const char *desc, unsigned diag_idx)
 {
@@ -420,8 +431,8 @@ epath_finder::explore_feasible_paths (const exploded_node *target_enode,
   {
     auto_checking_feasibility sentinel (mgr);
 
-    while (process_worklist_item (&worklist, tg, &fg, target_enode, pd,
-                                 diag_idx, &best_path))
+    while (process_worklist_item (&worklist, tg, &fg, target_enode, target_stmt,
+                                 pd, diag_idx, &best_path))
       {
        /* Empty; the work is done within process_worklist_item.  */
       }
@@ -465,6 +476,7 @@ process_worklist_item (feasible_worklist *worklist,
                       const trimmed_graph &tg,
                       feasible_graph *fg,
                       const exploded_node *target_enode,
+                      const gimple *target_stmt,
                       const pending_diagnostic &pd,
                       unsigned diag_idx,
                       std::unique_ptr<exploded_path> *out_best_path) const
@@ -523,7 +535,7 @@ process_worklist_item (feasible_worklist *worklist,
                             " (length: %i)",
                             target_enode->m_index, diag_idx,
                             succ_fnode->get_path_length ());
-             if (!pd.check_valid_fpath_p (succ_fnode))
+             if (!pd.check_valid_fpath_p (*succ_fnode, target_stmt))
                {
                  if (logger)
                    logger->log ("rejecting feasible path due to"
@@ -824,7 +836,8 @@ saved_diagnostic::calc_best_epath (epath_finder *pf)
   LOG_SCOPE (logger);
   m_problem = NULL;
 
-  m_best_epath = pf->get_best_epath (m_enode, *m_d, m_d->get_kind (), m_idx,
+  m_best_epath = pf->get_best_epath (m_enode, m_stmt,
+                                    *m_d, m_d->get_kind (), m_idx,
                                     &m_problem);
 
   /* Handle failure to find a feasible path.  */
index 8982d9d2a11799b9c77f0c2081885671ac703792..24ded2670197901c5d64efce85dd07ff7fa9a2f3 100644 (file)
@@ -4823,17 +4823,7 @@ feasibility_state::maybe_update_for_edge (logger *logger,
       auto_cfun sentinel (src_point.get_function ());
       input_location = stmt->location;
 
-      if (const gassign *assign = dyn_cast <const gassign *> (stmt))
-       m_model.on_assignment (assign, NULL);
-      else if (const gasm *asm_stmt = dyn_cast <const gasm *> (stmt))
-       m_model.on_asm_stmt (asm_stmt, NULL);
-      else if (const gcall *call = dyn_cast <const gcall *> (stmt))
-       {
-         bool unknown_side_effects = m_model.on_call_pre (call, NULL);
-         m_model.on_call_post (call, unknown_side_effects, NULL);
-       }
-      else if (const greturn *return_ = dyn_cast <const greturn *> (stmt))
-       m_model.on_return (return_, NULL);
+      update_for_stmt (stmt);
     }
 
   const superedge *sedge = eedge->m_sedge;
@@ -4910,6 +4900,24 @@ feasibility_state::maybe_update_for_edge (logger *logger,
   return true;
 }
 
+/* Update this object for the effects of STMT.  */
+
+void
+feasibility_state::update_for_stmt (const gimple *stmt)
+{
+  if (const gassign *assign = dyn_cast <const gassign *> (stmt))
+    m_model.on_assignment (assign, NULL);
+  else if (const gasm *asm_stmt = dyn_cast <const gasm *> (stmt))
+    m_model.on_asm_stmt (asm_stmt, NULL);
+  else if (const gcall *call = dyn_cast <const gcall *> (stmt))
+    {
+      bool unknown_side_effects = m_model.on_call_pre (call, NULL);
+      m_model.on_call_post (call, unknown_side_effects, NULL);
+    }
+  else if (const greturn *return_ = dyn_cast <const greturn *> (stmt))
+    m_model.on_return (return_, NULL);
+}
+
 /* Dump this object to PP.  */
 
 void
index f73e05704ad17c5ce6b19d3b051d313b273dc034..4a4ef9d12b486326c0a7154fa13f493746049d9d 100644 (file)
@@ -973,6 +973,7 @@ public:
   bool maybe_update_for_edge (logger *logger,
                              const exploded_edge *eedge,
                              rejected_constraint **out_rc);
+  void update_for_stmt (const gimple *stmt);
 
   const region_model &get_model () const { return m_model; }
   const auto_sbitmap &get_snodes_visited () const { return m_snodes_visited; }
index 78ade492a4cb22bfa4797cf2260003fa0530305f..d6ff1a3694ae60259add019bb69e8fdfac54c03b 100644 (file)
@@ -104,6 +104,36 @@ feasible_node::dump_dot (graphviz_out *gv,
   pp_flush (pp);
 }
 
+/* Attempt to get the region_model for this node's state at TARGET_STMT.
+   Return true and write to *OUT if found.
+   Return false if there's a problem.  */
+
+bool
+feasible_node::get_state_at_stmt (const gimple *target_stmt,
+                                 region_model *out) const
+{
+  if (!target_stmt)
+    return false;
+
+  feasibility_state result (m_state);
+
+  /* Update state for the stmts that were processed in each enode.  */
+  for (unsigned stmt_idx = 0; stmt_idx < m_inner_node->m_num_processed_stmts;
+       stmt_idx++)
+    {
+      const gimple *stmt = m_inner_node->get_processed_stmt (stmt_idx);
+      if (stmt == target_stmt)
+       {
+         *out = result.get_model ();
+         return true;
+       }
+      result.update_for_stmt (stmt);
+    }
+
+  /* TARGET_STMT not found; wrong node?  */
+  return false;
+}
+
 /* Implementation of dump_dot vfunc for infeasible_node.
    In particular, show the rejected constraint.  */
 
index 2bbee01597f33f0565636ce7d13885a31b524e83..0da7265979beb4b3b6610c82ed524175a7e8316d 100644 (file)
@@ -21,6 +21,8 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_ANALYZER_FEASIBLE_GRAPH_H
 #define GCC_ANALYZER_FEASIBLE_GRAPH_H
 
+#include "analyzer/exploded-graph.h"
+
 namespace ana {
 
 /* Forward decls.  */
@@ -102,6 +104,9 @@ public:
 
   unsigned get_path_length () const { return m_path_length; }
 
+  bool get_state_at_stmt (const gimple *target_stmt,
+                         region_model *out) const;
+
 private:
   feasibility_state m_state;
   unsigned m_path_length;
index 8d101d14fd0034f3b235b60b4eba850bf6eb5f70..1886534313eb6083955ecf99da005ab70c271686 100644 (file)
@@ -203,18 +203,19 @@ public:
   /* Reject paths in which conjured svalues have affected control flow
      since m_prev_entry_enode.  */
 
-  bool check_valid_fpath_p (const feasible_node *final_fnode)
+  bool check_valid_fpath_p (const feasible_node &final_fnode,
+                           const gimple *)
     const final override
   {
     /* Reject paths in which calls with unknown side effects have occurred
        since m_prev_entry_enode.
        Find num calls with side effects.  Walk backward until we reach the
        pref */
-    gcc_assert (final_fnode->get_inner_node () == m_new_entry_enode);
+    gcc_assert (final_fnode.get_inner_node () == m_new_entry_enode);
 
     /* FG is actually a tree.  Walk backwards from FINAL_FNODE until we
        reach the prev_entry_enode (or the origin).  */
-    const feasible_node *iter_fnode = final_fnode;
+    const feasible_node *iter_fnode = &final_fnode;
     while (iter_fnode->get_inner_node ()->m_index != 0)
       {
        gcc_assert (iter_fnode->m_preds.length () == 1);
index b919d5b9099192b901e7cc278d994dee870175b9..d9e9e7f89052b15fa148fc4e0409c1198dd98273 100644 (file)
@@ -351,7 +351,8 @@ class pending_diagnostic
   /* Vfunc to give diagnostic subclasses the opportunity to reject diagnostics
      by imposing their own additional feasibility checks on the path to a
      given feasible_node.  */
-  virtual bool check_valid_fpath_p (const feasible_node *) const
+  virtual bool check_valid_fpath_p (const feasible_node &,
+                                   const gimple *) const
   {
     /* Default implementation: accept this path.  */
     return true;
index adb6a5e06df917a8ea96a32b65b915c1ea7bb74b..424d83c191f94ac5f6d58ee0a4221e2d57bae605 100644 (file)
@@ -76,6 +76,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gcc-rich-location.h"
 #include "analyzer/checker-event.h"
 #include "analyzer/checker-path.h"
+#include "analyzer/feasible-graph.h"
 
 #if ENABLE_ANALYZER
 
@@ -468,9 +469,11 @@ class poisoned_value_diagnostic
 {
 public:
   poisoned_value_diagnostic (tree expr, enum poison_kind pkind,
-                            const region *src_region)
+                            const region *src_region,
+                            tree check_expr)
   : m_expr (expr), m_pkind (pkind),
-    m_src_region (src_region)
+    m_src_region (src_region),
+    m_check_expr (check_expr)
   {}
 
   const char *get_kind () const final override { return "poisoned_value_diagnostic"; }
@@ -563,10 +566,47 @@ public:
       interest->add_region_creation (m_src_region);
   }
 
+  /* Attempt to suppress false positives.
+     Reject paths where the value of the underlying region isn't poisoned.
+     This can happen due to state merging when exploring the exploded graph,
+     where the more precise analysis during feasibility analysis finds that
+     the region is in fact valid.
+     To do this we need to get the value from the fgraph.  Unfortunately
+     we can't simply query the state of m_src_region (from the enode),
+     since it might be a different region in the fnode state (e.g. with
+     heap-allocated regions, the numbering could be different).
+     Hence we access m_check_expr, if available.  */
+
+  bool check_valid_fpath_p (const feasible_node &fnode,
+                           const gimple *emission_stmt)
+    const final override
+  {
+    if (!m_check_expr)
+      return true;
+
+    /* We've reached the enode, but not necessarily the right function_point.
+       Try to get the state at the correct stmt.  */
+    region_model emission_model (fnode.get_model ().get_manager());
+    if (!fnode.get_state_at_stmt (emission_stmt, &emission_model))
+      /* Couldn't get state; accept this diagnostic.  */
+      return true;
+
+    const svalue *fsval = emission_model.get_rvalue (m_check_expr, NULL);
+    /* Check to see if the expr is also poisoned in FNODE (and in the
+       same way).  */
+    const poisoned_svalue * fspval = fsval->dyn_cast_poisoned_svalue ();
+    if (!fspval)
+      return false;
+    if (fspval->get_poison_kind () != m_pkind)
+      return false;
+    return true;
+  }
+
 private:
   tree m_expr;
   enum poison_kind m_pkind;
   const region *m_src_region;
+  tree m_check_expr;
 };
 
 /* A subclass of pending_diagnostic for complaining about shifts
@@ -1050,9 +1090,22 @@ region_model::check_for_poison (const svalue *sval,
       tree diag_arg = fixup_tree_for_diagnostic (expr);
       if (src_region == NULL && pkind == POISON_KIND_UNINIT)
        src_region = get_region_for_poisoned_expr (expr);
+
+      /* Can we reliably get the poisoned value from "expr"?
+        This is for use by poisoned_value_diagnostic::check_valid_fpath_p.
+        Unfortunately, we might not have a reliable value for EXPR.
+        Hence we only query its value now, and only use it if we get the
+        poisoned value back again.  */
+      tree check_expr = expr;
+      const svalue *foo_sval = get_rvalue (expr, NULL);
+      if (foo_sval == sval)
+       check_expr = expr;
+      else
+       check_expr = NULL;
       if (ctxt->warn (make_unique<poisoned_value_diagnostic> (diag_arg,
                                                              pkind,
-                                                             src_region)))
+                                                             src_region,
+                                                             check_expr)))
        {
          /* We only want to report use of a poisoned value at the first
             place it gets used; return an unknown value to avoid generating
@@ -2486,7 +2539,7 @@ region_model::deref_rvalue (const svalue *ptr_sval, tree ptr_tree,
                  = as_a <const poisoned_svalue *> (ptr_sval);
                enum poison_kind pkind = poisoned_sval->get_poison_kind ();
                ctxt->warn (make_unique<poisoned_value_diagnostic>
-                             (ptr, pkind, NULL));
+                             (ptr, pkind, NULL, NULL));
              }
          }
       }
@@ -5010,6 +5063,16 @@ region_model::get_or_create_region_for_heap_alloc (const svalue *size_in_bytes,
      this path.  */
   auto_bitmap base_regs_in_use;
   get_referenced_base_regions (base_regs_in_use);
+
+  /* Don't reuse regions that are marked as TOUCHED.  */
+  for (store::cluster_map_t::iterator iter = m_store.begin ();
+       iter != m_store.end (); ++iter)
+    if ((*iter).second->touched_p ())
+      {
+       const region *base_reg = (*iter).first;
+       bitmap_set_bit (base_regs_in_use, base_reg->get_id ());
+      }
+
   const region *reg
     = m_mgr->get_or_create_region_for_heap_alloc (base_regs_in_use);
   if (size_in_bytes)
diff --git a/gcc/testsuite/gcc.dg/analyzer/coreutils-cksum-pr108664.c b/gcc/testsuite/gcc.dg/analyzer/coreutils-cksum-pr108664.c
new file mode 100644 (file)
index 0000000..27eef83
--- /dev/null
@@ -0,0 +1,80 @@
+/* Reduced from coreutils's cksum.c: cksum_slice8 */
+
+typedef long unsigned int size_t;
+typedef unsigned int __uint32_t;
+typedef unsigned long int __uintmax_t;
+typedef struct _IO_FILE FILE;
+extern size_t
+fread_unlocked(void* __restrict __ptr,
+               size_t __size,
+               size_t __n,
+               FILE* __restrict __stream);
+extern int
+feof_unlocked(FILE* __stream) __attribute__((__nothrow__, __leaf__));
+extern int
+ferror_unlocked(FILE* __stream) __attribute__((__nothrow__, __leaf__));
+static __inline __uint32_t
+__bswap_32(__uint32_t __bsx)
+{
+
+  return __builtin_bswap32(__bsx);
+}
+typedef __uint32_t uint32_t;
+typedef unsigned long int uint_fast32_t;
+typedef __uintmax_t uintmax_t;
+extern int*
+__errno_location(void) __attribute__((__nothrow__, __leaf__))
+__attribute__((__const__));
+extern uint_fast32_t const crctab[8][256];
+
+static _Bool
+cksum_slice8(FILE* fp, uint_fast32_t* crc_out, uintmax_t* length_out)
+{
+  uint32_t buf[(1 << 16) / sizeof(uint32_t)];
+  uint_fast32_t crc = 0;
+  uintmax_t length = 0;
+  size_t bytes_read;
+
+  if (!fp || !crc_out || !length_out)
+    return 0;
+
+  while ((bytes_read = fread_unlocked(buf, 1, (1 << 16), fp)) > 0) {
+    uint32_t* datap;
+
+    if (length + bytes_read < length) {
+
+      (*__errno_location()) = 75;
+      return 0;
+    }
+    length += bytes_read;
+
+    if (bytes_read == 0) {
+      if (ferror_unlocked(fp))
+        return 0;
+    }
+
+    datap = (uint32_t*)buf;
+    while (bytes_read >= 8) {
+      uint32_t first = *datap++, second = *datap++; /* { dg-bogus "use of uninitialized value" "PR analyzer/108664" } */
+      crc ^= __bswap_32(first);
+      second = __bswap_32(second);
+      crc =
+        (crctab[7][(crc >> 24) & 0xFF] ^ crctab[6][(crc >> 16) & 0xFF] ^
+         crctab[5][(crc >> 8) & 0xFF] ^ crctab[4][(crc)&0xFF] ^
+         crctab[3][(second >> 24) & 0xFF] ^ crctab[2][(second >> 16) & 0xFF] ^
+         crctab[1][(second >> 8) & 0xFF] ^ crctab[0][(second)&0xFF]);
+      bytes_read -= 8;
+    }
+
+    unsigned char* cp = (unsigned char*)datap;
+    while (bytes_read--)
+      crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ *cp++) & 0xFF];
+    if (feof_unlocked(fp))
+      break;
+  }
+
+  *crc_out = crc;
+  *length_out = length;
+
+  return 1;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/coreutils-sum-pr108666.c b/gcc/testsuite/gcc.dg/analyzer/coreutils-sum-pr108666.c
new file mode 100644 (file)
index 0000000..9d13fce
--- /dev/null
@@ -0,0 +1,98 @@
+/* Reduced from coreutils's sum.c: bsd_sum_stream */
+
+typedef long unsigned int size_t;
+typedef unsigned char __uint8_t;
+typedef unsigned long int __uintmax_t;
+typedef struct _IO_FILE FILE;
+extern size_t
+fread_unlocked(void* __restrict __ptr,
+               size_t __size,
+               size_t __n,
+               FILE* __restrict __stream);
+extern int
+feof_unlocked(FILE* __stream) __attribute__((__nothrow__, __leaf__));
+extern int
+ferror_unlocked(FILE* __stream) __attribute__((__nothrow__, __leaf__));
+extern void*
+memcpy(void* __restrict __dest, const void* __restrict __src, size_t __n)
+  __attribute__((__nothrow__, __leaf__)) __attribute__((__nonnull__(1, 2)));
+extern void
+rpl_free(void*);
+extern int*
+__errno_location(void) __attribute__((__nothrow__, __leaf__))
+__attribute__((__const__));
+extern void*
+malloc(size_t __size) __attribute__((__nothrow__, __leaf__))
+__attribute__((__malloc__)) __attribute__((__alloc_size__(1)));
+typedef __uint8_t uint8_t;
+typedef __uintmax_t uintmax_t;
+
+int
+bsd_sum_stream(FILE* stream, void* resstream, uintmax_t* length)
+{
+  int ret = -1;
+  size_t sum, n;
+  int checksum = 0;
+  uintmax_t total_bytes = 0;
+  static const size_t buffer_length = 32768;
+  uint8_t* buffer = malloc(buffer_length);
+
+  if (!buffer)
+    return -1;
+
+  while (1) {
+    sum = 0;
+
+    while (1) {
+      n = fread_unlocked(buffer + sum, 1, buffer_length - sum, stream);
+      sum += n;
+
+      if (buffer_length == sum)
+        break;
+
+      if (n == 0) {
+        if (ferror_unlocked(stream))
+          goto cleanup_buffer;
+        goto final_process;
+      }
+
+      if (feof_unlocked(stream))
+        goto final_process;
+    }
+
+    for (size_t i = 0; i < sum; i++) {
+      checksum = (checksum >> 1) + ((checksum & 1) << 15);
+      checksum += buffer[i]; /* { dg-bogus "use of uninitialized value" "PR analyzer/108666" } */
+      checksum &= 0xffff;
+    }
+    if (total_bytes + sum < total_bytes) {
+
+      (*__errno_location()) = 75;
+      goto cleanup_buffer;
+    }
+    total_bytes += sum;
+  }
+
+final_process:;
+
+  for (size_t i = 0; i < sum; i++) {
+    checksum = (checksum >> 1) + ((checksum & 1) << 15);
+    checksum += buffer[i];  /* { dg-bogus "use of uninitialized value" "PR analyzer/108666" } */
+    checksum &= 0xffff;
+  }
+  if (total_bytes + sum < total_bytes) {
+
+    (*__errno_location()) = 75;
+    goto cleanup_buffer;
+  }
+  total_bytes += sum;
+
+  memcpy(resstream, &checksum, sizeof checksum);
+  *length = total_bytes;
+  ret = 0;
+cleanup_buffer:
+
+  rpl_free(buffer);
+  return ret;
+}
+
diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/uninit-pr108725.c b/gcc/testsuite/gcc.dg/analyzer/torture/uninit-pr108725.c
new file mode 100644 (file)
index 0000000..33982f0
--- /dev/null
@@ -0,0 +1,19 @@
+/* Reduced from an example in qemu-7.2.0: dump/win_dump.c  */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+extern int cpu_memory_rw_debug (bool x64, void *ptr);
+
+int cpu_read_ptr(bool x64, uint64_t *ptr)
+{
+    int ret;
+    uint32_t ptr32;
+    uint64_t ptr64;
+
+    ret = cpu_memory_rw_debug(x64, x64 ? (void *)&ptr64 : (void *)&ptr32);
+
+    *ptr = x64 ? ptr64 : ptr32; /* { dg-bogus "use of uninitialized value" } */
+
+    return ret;
+}