public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r13-6064] analyzer: fix uninit false +ves [PR108664, PR108666, PR108725]
@ 2023-02-15 19:53 David Malcolm
  0 siblings, 0 replies; only message in thread
From: David Malcolm @ 2023-02-15 19:53 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:b03a10b0b25cef4928ccead4c8a461d3674dbe86

commit r13-6064-gb03a10b0b25cef4928ccead4c8a461d3674dbe86
Author: David Malcolm <dmalcolm@redhat.com>
Date:   Wed Feb 15 14:52:02 2023 -0500

    analyzer: fix uninit false +ves [PR108664,PR108666,PR108725]
    
    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>

Diff:
---
 gcc/analyzer/diagnostic-manager.cc                 | 23 +++--
 gcc/analyzer/engine.cc                             | 30 ++++---
 gcc/analyzer/exploded-graph.h                      |  1 +
 gcc/analyzer/feasible-graph.cc                     | 30 +++++++
 gcc/analyzer/feasible-graph.h                      |  5 ++
 gcc/analyzer/infinite-recursion.cc                 |  7 +-
 gcc/analyzer/pending-diagnostic.h                  |  3 +-
 gcc/analyzer/region-model.cc                       | 71 +++++++++++++++-
 .../gcc.dg/analyzer/coreutils-cksum-pr108664.c     | 80 ++++++++++++++++++
 .../gcc.dg/analyzer/coreutils-sum-pr108666.c       | 98 ++++++++++++++++++++++
 .../gcc.dg/analyzer/torture/uninit-pr108725.c      | 19 +++++
 11 files changed, 343 insertions(+), 24 deletions(-)

diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
index 4f036a6c28a..0a447f7ba26 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -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.  */
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 8982d9d2a11..24ded267019 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -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
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index f73e05704ad..4a4ef9d12b4 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -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; }
diff --git a/gcc/analyzer/feasible-graph.cc b/gcc/analyzer/feasible-graph.cc
index 78ade492a4c..d6ff1a3694a 100644
--- a/gcc/analyzer/feasible-graph.cc
+++ b/gcc/analyzer/feasible-graph.cc
@@ -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.  */
 
diff --git a/gcc/analyzer/feasible-graph.h b/gcc/analyzer/feasible-graph.h
index 2bbee01597f..0da7265979b 100644
--- a/gcc/analyzer/feasible-graph.h
+++ b/gcc/analyzer/feasible-graph.h
@@ -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;
diff --git a/gcc/analyzer/infinite-recursion.cc b/gcc/analyzer/infinite-recursion.cc
index 8d101d14fd0..1886534313e 100644
--- a/gcc/analyzer/infinite-recursion.cc
+++ b/gcc/analyzer/infinite-recursion.cc
@@ -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);
diff --git a/gcc/analyzer/pending-diagnostic.h b/gcc/analyzer/pending-diagnostic.h
index b919d5b9099..d9e9e7f8905 100644
--- a/gcc/analyzer/pending-diagnostic.h
+++ b/gcc/analyzer/pending-diagnostic.h
@@ -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;
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index adb6a5e06df..424d83c191f 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -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
index 00000000000..27eef8369e2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/coreutils-cksum-pr108664.c
@@ -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
index 00000000000..9d13fce8531
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/coreutils-sum-pr108666.c
@@ -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
index 00000000000..33982f0078b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/torture/uninit-pr108725.c
@@ -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;
+}

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2023-02-15 19:53 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-15 19:53 [gcc r13-6064] analyzer: fix uninit false +ves [PR108664, PR108666, PR108725] David Malcolm

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).