[committed,1/7] analyzer: move bounds checking to a new bounds-checking.cc

Message ID 20221201024200.3722982-1-dmalcolm@redhat.com
State Committed
Headers
Series [committed,1/7] analyzer: move bounds checking to a new bounds-checking.cc |

Commit Message

David Malcolm Dec. 1, 2022, 2:41 a.m. UTC
  Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r13-4425-gb82b361af888a1.

gcc/ChangeLog:
	* Makefile.in (ANALYZER_OBJS): Add analyzer/bounds-checking.o.

gcc/analyzer/ChangeLog:
	* bounds-checking.cc: New file, taken from region-model.cc.
	* region-model.cc (class out_of_bounds): Move to
	bounds-checking.cc.
	(class past_the_end): Likewise.
	(class buffer_overflow): Likewise.
	(class buffer_overread): Likewise.
	(class buffer_underflow): Likewise.
	(class buffer_underread): Likewise.
	(class symbolic_past_the_end): Likewise.
	(class symbolic_buffer_overflow): Likewise.
	(class symbolic_buffer_overread): Likewise.
	(region_model::check_symbolic_bounds): Likewise.
	(maybe_get_integer_cst_tree): Likewise.
	(region_model::check_region_bounds): Likewise.
	* region-model.h: Add comment.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
 gcc/Makefile.in                 |   1 +
 gcc/analyzer/bounds-checking.cc | 695 ++++++++++++++++++++++++++++++++
 gcc/analyzer/region-model.cc    | 653 ------------------------------
 gcc/analyzer/region-model.h     |   2 +
 4 files changed, 698 insertions(+), 653 deletions(-)
 create mode 100644 gcc/analyzer/bounds-checking.cc
  

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index fa5e5b444bb..615a07089ee 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1255,6 +1255,7 @@  ANALYZER_OBJS = \
 	analyzer/analyzer-pass.o \
 	analyzer/analyzer-selftests.o \
 	analyzer/bar-chart.o \
+	analyzer/bounds-checking.o \
 	analyzer/call-info.o \
 	analyzer/call-string.o \
 	analyzer/call-summary.o \
diff --git a/gcc/analyzer/bounds-checking.cc b/gcc/analyzer/bounds-checking.cc
new file mode 100644
index 00000000000..19aaa51e6a8
--- /dev/null
+++ b/gcc/analyzer/bounds-checking.cc
@@ -0,0 +1,695 @@ 
+/* Bounds-checking of reads and writes to memory regions.
+   Copyright (C) 2019-2022 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#define INCLUDE_MEMORY
+#include "system.h"
+#include "coretypes.h"
+#include "make-unique.h"
+#include "tree.h"
+#include "function.h"
+#include "basic-block.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "diagnostic-core.h"
+#include "diagnostic-metadata.h"
+#include "analyzer/analyzer.h"
+#include "analyzer/analyzer-logging.h"
+#include "analyzer/region-model.h"
+
+#if ENABLE_ANALYZER
+
+namespace ana {
+
+/* Abstract base class for all out-of-bounds warnings with concrete values.  */
+
+class out_of_bounds : public pending_diagnostic_subclass<out_of_bounds>
+{
+public:
+  out_of_bounds (const region *reg, tree diag_arg,
+		 byte_range out_of_bounds_range)
+  : m_reg (reg), m_diag_arg (diag_arg),
+    m_out_of_bounds_range (out_of_bounds_range)
+  {}
+
+  const char *get_kind () const final override
+  {
+    return "out_of_bounds_diagnostic";
+  }
+
+  bool operator== (const out_of_bounds &other) const
+  {
+    return m_reg == other.m_reg
+	   && m_out_of_bounds_range == other.m_out_of_bounds_range
+	   && pending_diagnostic::same_tree_p (m_diag_arg, other.m_diag_arg);
+  }
+
+  int get_controlling_option () const final override
+  {
+    return OPT_Wanalyzer_out_of_bounds;
+  }
+
+  void mark_interesting_stuff (interesting_t *interest) final override
+  {
+    interest->add_region_creation (m_reg);
+  }
+
+protected:
+  const region *m_reg;
+  tree m_diag_arg;
+  byte_range m_out_of_bounds_range;
+};
+
+/* Abstract subclass to complaing about out-of-bounds
+   past the end of the buffer.  */
+
+class past_the_end : public out_of_bounds
+{
+public:
+  past_the_end (const region *reg, tree diag_arg, byte_range range,
+		tree byte_bound)
+  : out_of_bounds (reg, diag_arg, range), m_byte_bound (byte_bound)
+  {}
+
+  bool operator== (const past_the_end &other) const
+  {
+    return out_of_bounds::operator== (other)
+	   && pending_diagnostic::same_tree_p (m_byte_bound,
+					       other.m_byte_bound);
+  }
+
+  label_text
+  describe_region_creation_event (const evdesc::region_creation &ev) final
+  override
+  {
+    if (m_byte_bound && TREE_CODE (m_byte_bound) == INTEGER_CST)
+      return ev.formatted_print ("capacity is %E bytes", m_byte_bound);
+
+    return label_text ();
+  }
+
+protected:
+  tree m_byte_bound;
+};
+
+/* Concrete subclass to complain about buffer overflows.  */
+
+class buffer_overflow : public past_the_end
+{
+public:
+  buffer_overflow (const region *reg, tree diag_arg,
+		   byte_range range, tree byte_bound)
+  : past_the_end (reg, diag_arg, range, byte_bound)
+  {}
+
+  bool emit (rich_location *rich_loc) final override
+  {
+    diagnostic_metadata m;
+    bool warned;
+    switch (m_reg->get_memory_space ())
+      {
+      default:
+	m.add_cwe (787);
+	warned = warning_meta (rich_loc, m, get_controlling_option (),
+			       "buffer overflow");
+	break;
+      case MEMSPACE_STACK:
+	m.add_cwe (121);
+	warned = warning_meta (rich_loc, m, get_controlling_option (),
+			       "stack-based buffer overflow");
+	break;
+      case MEMSPACE_HEAP:
+	m.add_cwe (122);
+	warned = warning_meta (rich_loc, m, get_controlling_option (),
+			       "heap-based buffer overflow");
+	break;
+      }
+
+    if (warned)
+      {
+	char num_bytes_past_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+	print_dec (m_out_of_bounds_range.m_size_in_bytes,
+		   num_bytes_past_buf, UNSIGNED);
+	if (m_diag_arg)
+	  inform (rich_loc->get_loc (), "write is %s bytes past the end"
+					" of %qE", num_bytes_past_buf,
+						   m_diag_arg);
+	else
+	  inform (rich_loc->get_loc (), "write is %s bytes past the end"
+					"of the region",
+					num_bytes_past_buf);
+      }
+
+    return warned;
+  }
+
+  label_text describe_final_event (const evdesc::final_event &ev)
+  final override
+  {
+    byte_size_t start = m_out_of_bounds_range.get_start_byte_offset ();
+    byte_size_t end = m_out_of_bounds_range.get_last_byte_offset ();
+    char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+    print_dec (start, start_buf, SIGNED);
+    char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+    print_dec (end, end_buf, SIGNED);
+
+    if (start == end)
+      {
+	if (m_diag_arg)
+	  return ev.formatted_print ("out-of-bounds write at byte %s but %qE"
+				     " ends at byte %E", start_buf, m_diag_arg,
+							 m_byte_bound);
+	return ev.formatted_print ("out-of-bounds write at byte %s but region"
+				   " ends at byte %E", start_buf,
+						       m_byte_bound);
+      }
+    else
+      {
+	if (m_diag_arg)
+	  return ev.formatted_print ("out-of-bounds write from byte %s till"
+				     " byte %s but %qE ends at byte %E",
+				     start_buf, end_buf, m_diag_arg,
+				     m_byte_bound);
+	return ev.formatted_print ("out-of-bounds write from byte %s till"
+				   " byte %s but region ends at byte %E",
+				   start_buf, end_buf, m_byte_bound);
+      }
+  }
+};
+
+/* Concrete subclass to complain about buffer overreads.  */
+
+class buffer_overread : public past_the_end
+{
+public:
+  buffer_overread (const region *reg, tree diag_arg,
+		   byte_range range, tree byte_bound)
+  : past_the_end (reg, diag_arg, range, byte_bound)
+  {}
+
+  bool emit (rich_location *rich_loc) final override
+  {
+    diagnostic_metadata m;
+    m.add_cwe (126);
+    bool warned = warning_meta (rich_loc, m, get_controlling_option (),
+				"buffer overread");
+
+    if (warned)
+      {
+	char num_bytes_past_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+	print_dec (m_out_of_bounds_range.m_size_in_bytes,
+		   num_bytes_past_buf, UNSIGNED);
+	if (m_diag_arg)
+	  inform (rich_loc->get_loc (), "read is %s bytes past the end"
+					" of %qE", num_bytes_past_buf,
+						    m_diag_arg);
+	else
+	  inform (rich_loc->get_loc (), "read is %s bytes past the end"
+					"of the region",
+					num_bytes_past_buf);
+      }
+
+    return warned;
+  }
+
+  label_text describe_final_event (const evdesc::final_event &ev)
+  final override
+  {
+    byte_size_t start = m_out_of_bounds_range.get_start_byte_offset ();
+    byte_size_t end = m_out_of_bounds_range.get_last_byte_offset ();
+    char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+    print_dec (start, start_buf, SIGNED);
+    char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+    print_dec (end, end_buf, SIGNED);
+
+    if (start == end)
+      {
+	if (m_diag_arg)
+	  return ev.formatted_print ("out-of-bounds read at byte %s but %qE"
+				     " ends at byte %E", start_buf, m_diag_arg,
+							 m_byte_bound);
+	return ev.formatted_print ("out-of-bounds read at byte %s but region"
+				   " ends at byte %E", start_buf,
+						       m_byte_bound);
+      }
+    else
+      {
+	if (m_diag_arg)
+	  return ev.formatted_print ("out-of-bounds read from byte %s till"
+				     " byte %s but %qE ends at byte %E",
+				     start_buf, end_buf, m_diag_arg,
+				     m_byte_bound);
+	return ev.formatted_print ("out-of-bounds read from byte %s till"
+				   " byte %s but region ends at byte %E",
+				   start_buf, end_buf, m_byte_bound);
+      }
+  }
+};
+
+/* Concrete subclass to complain about buffer underflows.  */
+
+class buffer_underflow : public out_of_bounds
+{
+public:
+  buffer_underflow (const region *reg, tree diag_arg, byte_range range)
+  : out_of_bounds (reg, diag_arg, range)
+  {}
+
+  bool emit (rich_location *rich_loc) final override
+  {
+    diagnostic_metadata m;
+    m.add_cwe (124);
+    return warning_meta (rich_loc, m, get_controlling_option (),
+			 "buffer underflow");
+  }
+
+  label_text describe_final_event (const evdesc::final_event &ev)
+  final override
+  {
+    byte_size_t start = m_out_of_bounds_range.get_start_byte_offset ();
+    byte_size_t end = m_out_of_bounds_range.get_last_byte_offset ();
+    char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+    print_dec (start, start_buf, SIGNED);
+    char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+    print_dec (end, end_buf, SIGNED);
+
+    if (start == end)
+      {
+	if (m_diag_arg)
+	  return ev.formatted_print ("out-of-bounds write at byte %s but %qE"
+				     " starts at byte 0", start_buf,
+							  m_diag_arg);
+	return ev.formatted_print ("out-of-bounds write at byte %s but region"
+				   " starts at byte 0", start_buf);
+      }
+    else
+      {
+	if (m_diag_arg)
+	  return ev.formatted_print ("out-of-bounds write from byte %s till"
+				     " byte %s but %qE starts at byte 0",
+				     start_buf, end_buf, m_diag_arg);
+	return ev.formatted_print ("out-of-bounds write from byte %s till"
+				   " byte %s but region starts at byte 0",
+				   start_buf, end_buf);;
+      }
+  }
+};
+
+/* Concrete subclass to complain about buffer underreads.  */
+
+class buffer_underread : public out_of_bounds
+{
+public:
+  buffer_underread (const region *reg, tree diag_arg, byte_range range)
+  : out_of_bounds (reg, diag_arg, range)
+  {}
+
+  bool emit (rich_location *rich_loc) final override
+  {
+    diagnostic_metadata m;
+    m.add_cwe (127);
+    return warning_meta (rich_loc, m, get_controlling_option (),
+			 "buffer underread");
+  }
+
+  label_text describe_final_event (const evdesc::final_event &ev)
+  final override
+  {
+    byte_size_t start = m_out_of_bounds_range.get_start_byte_offset ();
+    byte_size_t end = m_out_of_bounds_range.get_last_byte_offset ();
+    char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+    print_dec (start, start_buf, SIGNED);
+    char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+    print_dec (end, end_buf, SIGNED);
+
+    if (start == end)
+      {
+	if (m_diag_arg)
+	  return ev.formatted_print ("out-of-bounds read at byte %s but %qE"
+				     " starts at byte 0", start_buf,
+							  m_diag_arg);
+	return ev.formatted_print ("out-of-bounds read at byte %s but region"
+				  " starts at byte 0", start_buf);
+      }
+    else
+      {
+	if (m_diag_arg)
+	  return ev.formatted_print ("out-of-bounds read from byte %s till"
+				     " byte %s but %qE starts at byte 0",
+				     start_buf, end_buf, m_diag_arg);
+	return ev.formatted_print ("out-of-bounds read from byte %s till"
+				   " byte %s but region starts at byte 0",
+				   start_buf, end_buf);;
+      }
+  }
+};
+
+/* Abstract class to complain about out-of-bounds read/writes where
+   the values are symbolic.  */
+
+class symbolic_past_the_end
+  : public pending_diagnostic_subclass<symbolic_past_the_end>
+{
+public:
+  symbolic_past_the_end (const region *reg, tree diag_arg, tree offset,
+			 tree num_bytes, tree capacity)
+  : m_reg (reg), m_diag_arg (diag_arg), m_offset (offset),
+    m_num_bytes (num_bytes), m_capacity (capacity)
+  {}
+
+  const char *get_kind () const final override
+  {
+    return "symbolic_past_the_end";
+  }
+
+  bool operator== (const symbolic_past_the_end &other) const
+  {
+    return m_reg == other.m_reg
+	   && pending_diagnostic::same_tree_p (m_diag_arg, other.m_diag_arg)
+	   && pending_diagnostic::same_tree_p (m_offset, other.m_offset)
+	   && pending_diagnostic::same_tree_p (m_num_bytes, other.m_num_bytes)
+	   && pending_diagnostic::same_tree_p (m_capacity, other.m_capacity);
+  }
+
+  int get_controlling_option () const final override
+  {
+    return OPT_Wanalyzer_out_of_bounds;
+  }
+
+  void mark_interesting_stuff (interesting_t *interest) final override
+  {
+    interest->add_region_creation (m_reg);
+  }
+
+  label_text
+  describe_region_creation_event (const evdesc::region_creation &ev) final
+  override
+  {
+    if (m_capacity)
+      return ev.formatted_print ("capacity is %qE bytes", m_capacity);
+
+    return label_text ();
+  }
+
+  label_text
+  describe_final_event (const evdesc::final_event &ev) final override
+  {
+    const char *byte_str;
+    if (pending_diagnostic::same_tree_p (m_num_bytes, integer_one_node))
+      byte_str = "byte";
+    else
+      byte_str = "bytes";
+
+    if (m_offset)
+      {
+	if (m_num_bytes && TREE_CODE (m_num_bytes) == INTEGER_CST)
+	  {
+	    if (m_diag_arg)
+	      return ev.formatted_print ("%s of %E %s at offset %qE"
+					 " exceeds %qE", m_dir_str,
+					 m_num_bytes, byte_str,
+					 m_offset, m_diag_arg);
+	    else
+	      return ev.formatted_print ("%s of %E %s at offset %qE"
+					 " exceeds the buffer", m_dir_str,
+					 m_num_bytes, byte_str, m_offset);
+	  }
+	else if (m_num_bytes)
+	  {
+	    if (m_diag_arg)
+	      return ev.formatted_print ("%s of %qE %s at offset %qE"
+					 " exceeds %qE", m_dir_str,
+					 m_num_bytes, byte_str,
+					 m_offset, m_diag_arg);
+	    else
+	      return ev.formatted_print ("%s of %qE %s at offset %qE"
+					 " exceeds the buffer", m_dir_str,
+					 m_num_bytes, byte_str, m_offset);
+	  }
+	else
+	  {
+	    if (m_diag_arg)
+	      return ev.formatted_print ("%s at offset %qE exceeds %qE",
+					 m_dir_str, m_offset, m_diag_arg);
+	    else
+	      return ev.formatted_print ("%s at offset %qE exceeds the"
+					 " buffer", m_dir_str, m_offset);
+	  }
+      }
+    if (m_diag_arg)
+      return ev.formatted_print ("out-of-bounds %s on %qE",
+				 m_dir_str, m_diag_arg);
+    return ev.formatted_print ("out-of-bounds %s", m_dir_str);
+  }
+
+protected:
+  const region *m_reg;
+  tree m_diag_arg;
+  tree m_offset;
+  tree m_num_bytes;
+  tree m_capacity;
+  const char *m_dir_str;
+};
+
+/* Concrete subclass to complain about overflows with symbolic values.  */
+
+class symbolic_buffer_overflow : public symbolic_past_the_end
+{
+public:
+  symbolic_buffer_overflow (const region *reg, tree diag_arg, tree offset,
+			    tree num_bytes, tree capacity)
+  : symbolic_past_the_end (reg, diag_arg, offset, num_bytes, capacity)
+  {
+    m_dir_str = "write";
+  }
+
+  bool emit (rich_location *rich_loc) final override
+  {
+    diagnostic_metadata m;
+    switch (m_reg->get_memory_space ())
+      {
+      default:
+	m.add_cwe (787);
+	return warning_meta (rich_loc, m, get_controlling_option (),
+			     "buffer overflow");
+      case MEMSPACE_STACK:
+	m.add_cwe (121);
+	return warning_meta (rich_loc, m, get_controlling_option (),
+			     "stack-based buffer overflow");
+      case MEMSPACE_HEAP:
+	m.add_cwe (122);
+	return warning_meta (rich_loc, m, get_controlling_option (),
+			     "heap-based buffer overflow");
+      }
+  }
+};
+
+/* Concrete subclass to complain about overreads with symbolic values.  */
+
+class symbolic_buffer_overread : public symbolic_past_the_end
+{
+public:
+  symbolic_buffer_overread (const region *reg, tree diag_arg, tree offset,
+			    tree num_bytes, tree capacity)
+  : symbolic_past_the_end (reg, diag_arg, offset, num_bytes, capacity)
+  {
+    m_dir_str = "read";
+  }
+
+  bool emit (rich_location *rich_loc) final override
+  {
+    diagnostic_metadata m;
+    m.add_cwe (126);
+    return warning_meta (rich_loc, m, get_controlling_option (),
+			 "buffer overread");
+  }
+};
+
+/* Check whether an access is past the end of the BASE_REG.  */
+
+void
+region_model::check_symbolic_bounds (const region *base_reg,
+				     const svalue *sym_byte_offset,
+				     const svalue *num_bytes_sval,
+				     const svalue *capacity,
+				     enum access_direction dir,
+				     region_model_context *ctxt) const
+{
+  gcc_assert (ctxt);
+
+  const svalue *next_byte
+    = m_mgr->get_or_create_binop (num_bytes_sval->get_type (), PLUS_EXPR,
+				  sym_byte_offset, num_bytes_sval);
+
+  if (eval_condition (next_byte, GT_EXPR, capacity).is_true ())
+    {
+      tree diag_arg = get_representative_tree (base_reg);
+      tree offset_tree = get_representative_tree (sym_byte_offset);
+      tree num_bytes_tree = get_representative_tree (num_bytes_sval);
+      tree capacity_tree = get_representative_tree (capacity);
+      switch (dir)
+	{
+	default:
+	  gcc_unreachable ();
+	  break;
+	case DIR_READ:
+	  ctxt->warn (make_unique<symbolic_buffer_overread> (base_reg,
+							     diag_arg,
+							     offset_tree,
+							     num_bytes_tree,
+							     capacity_tree));
+	  break;
+	case DIR_WRITE:
+	  ctxt->warn (make_unique<symbolic_buffer_overflow> (base_reg,
+							     diag_arg,
+							     offset_tree,
+							     num_bytes_tree,
+							     capacity_tree));
+	  break;
+	}
+    }
+}
+
+static tree
+maybe_get_integer_cst_tree (const svalue *sval)
+{
+  tree cst_tree = sval->maybe_get_constant ();
+  if (cst_tree && TREE_CODE (cst_tree) == INTEGER_CST)
+    return cst_tree;
+
+  return NULL_TREE;
+}
+
+/* May complain when the access on REG is out-of-bounds.  */
+
+void
+region_model::check_region_bounds (const region *reg,
+				   enum access_direction dir,
+				   region_model_context *ctxt) const
+{
+  gcc_assert (ctxt);
+
+  /* Get the offset.  */
+  region_offset reg_offset = reg->get_offset (m_mgr);
+  const region *base_reg = reg_offset.get_base_region ();
+
+  /* Bail out on symbolic regions.
+     (e.g. because the analyzer did not see previous offsets on the latter,
+     it might think that a negative access is before the buffer).  */
+  if (base_reg->symbolic_p ())
+    return;
+
+  /* Find out how many bytes were accessed.  */
+  const svalue *num_bytes_sval = reg->get_byte_size_sval (m_mgr);
+  tree num_bytes_tree = maybe_get_integer_cst_tree (num_bytes_sval);
+  /* Bail out if 0 bytes are accessed.  */
+  if (num_bytes_tree && zerop (num_bytes_tree))
+    return;
+
+  /* Get the capacity of the buffer.  */
+  const svalue *capacity = get_capacity (base_reg);
+  tree cst_capacity_tree = maybe_get_integer_cst_tree (capacity);
+
+  /* The constant offset from a pointer is represented internally as a sizetype
+     but should be interpreted as a signed value here.  The statement below
+     converts the offset from bits to bytes and then to a signed integer with
+     the same precision the sizetype has on the target system.
+
+     For example, this is needed for out-of-bounds-3.c test1 to pass when
+     compiled with a 64-bit gcc build targeting 32-bit systems.  */
+  byte_offset_t offset;
+  if (!reg_offset.symbolic_p ())
+    offset = wi::sext (reg_offset.get_bit_offset () >> LOG2_BITS_PER_UNIT,
+		       TYPE_PRECISION (size_type_node));
+
+  /* If either the offset or the number of bytes accessed are symbolic,
+     we have to reason about symbolic values.  */
+  if (reg_offset.symbolic_p () || !num_bytes_tree)
+    {
+      const svalue* byte_offset_sval;
+      if (!reg_offset.symbolic_p ())
+	{
+	  tree offset_tree = wide_int_to_tree (integer_type_node, offset);
+	  byte_offset_sval
+	    = m_mgr->get_or_create_constant_svalue (offset_tree);
+	}
+      else
+	byte_offset_sval = reg_offset.get_symbolic_byte_offset ();
+      check_symbolic_bounds (base_reg, byte_offset_sval, num_bytes_sval,
+			     capacity, dir, ctxt);
+      return;
+    }
+
+  /* Otherwise continue to check with concrete values.  */
+  byte_range out (0, 0);
+  /* NUM_BYTES_TREE should always be interpreted as unsigned.  */
+  byte_offset_t num_bytes_unsigned = wi::to_offset (num_bytes_tree);
+  byte_range read_bytes (offset, num_bytes_unsigned);
+  /* If read_bytes has a subset < 0, we do have an underflow.  */
+  if (read_bytes.falls_short_of_p (0, &out))
+    {
+      tree diag_arg = get_representative_tree (base_reg);
+      switch (dir)
+	{
+	default:
+	  gcc_unreachable ();
+	  break;
+	case DIR_READ:
+	  ctxt->warn (make_unique<buffer_underread> (reg, diag_arg, out));
+	  break;
+	case DIR_WRITE:
+	  ctxt->warn (make_unique<buffer_underflow> (reg, diag_arg, out));
+	  break;
+	}
+    }
+
+  /* For accesses past the end, we do need a concrete capacity.  No need to
+     do a symbolic check here because the inequality check does not reason
+     whether constants are greater than symbolic values.  */
+  if (!cst_capacity_tree)
+    return;
+
+  byte_range buffer (0, wi::to_offset (cst_capacity_tree));
+  /* If READ_BYTES exceeds BUFFER, we do have an overflow.  */
+  if (read_bytes.exceeds_p (buffer, &out))
+    {
+      tree byte_bound = wide_int_to_tree (size_type_node,
+					  buffer.get_next_byte_offset ());
+      tree diag_arg = get_representative_tree (base_reg);
+
+      switch (dir)
+	{
+	default:
+	  gcc_unreachable ();
+	  break;
+	case DIR_READ:
+	  ctxt->warn (make_unique<buffer_overread> (reg, diag_arg,
+						    out, byte_bound));
+	  break;
+	case DIR_WRITE:
+	  ctxt->warn (make_unique<buffer_overflow> (reg, diag_arg,
+						    out, byte_bound));
+	  break;
+	}
+    }
+}
+
+} // namespace ana
+
+#endif /* #if ENABLE_ANALYZER */
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 7f2c0b6bd1a..91b868f7b16 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -1212,659 +1212,6 @@  region_model::on_stmt_pre (const gimple *stmt,
     }
 }
 
-/* Abstract base class for all out-of-bounds warnings with concrete values.  */
-
-class out_of_bounds : public pending_diagnostic_subclass<out_of_bounds>
-{
-public:
-  out_of_bounds (const region *reg, tree diag_arg,
-		 byte_range out_of_bounds_range)
-  : m_reg (reg), m_diag_arg (diag_arg),
-    m_out_of_bounds_range (out_of_bounds_range)
-  {}
-
-  const char *get_kind () const final override
-  {
-    return "out_of_bounds_diagnostic";
-  }
-
-  bool operator== (const out_of_bounds &other) const
-  {
-    return m_reg == other.m_reg
-	   && m_out_of_bounds_range == other.m_out_of_bounds_range
-	   && pending_diagnostic::same_tree_p (m_diag_arg, other.m_diag_arg);
-  }
-
-  int get_controlling_option () const final override
-  {
-    return OPT_Wanalyzer_out_of_bounds;
-  }
-
-  void mark_interesting_stuff (interesting_t *interest) final override
-  {
-    interest->add_region_creation (m_reg);
-  }
-
-protected:
-  const region *m_reg;
-  tree m_diag_arg;
-  byte_range m_out_of_bounds_range;
-};
-
-/* Abstract subclass to complaing about out-of-bounds
-   past the end of the buffer.  */
-
-class past_the_end : public out_of_bounds
-{
-public:
-  past_the_end (const region *reg, tree diag_arg, byte_range range,
-		tree byte_bound)
-  : out_of_bounds (reg, diag_arg, range), m_byte_bound (byte_bound)
-  {}
-
-  bool operator== (const past_the_end &other) const
-  {
-    return out_of_bounds::operator== (other)
-	   && pending_diagnostic::same_tree_p (m_byte_bound,
-					       other.m_byte_bound);
-  }
-
-  label_text
-  describe_region_creation_event (const evdesc::region_creation &ev) final
-  override
-  {
-    if (m_byte_bound && TREE_CODE (m_byte_bound) == INTEGER_CST)
-      return ev.formatted_print ("capacity is %E bytes", m_byte_bound);
-
-    return label_text ();
-  }
-
-protected:
-  tree m_byte_bound;
-};
-
-/* Concrete subclass to complain about buffer overflows.  */
-
-class buffer_overflow : public past_the_end
-{
-public:
-  buffer_overflow (const region *reg, tree diag_arg,
-		   byte_range range, tree byte_bound)
-  : past_the_end (reg, diag_arg, range, byte_bound)
-  {}
-
-  bool emit (rich_location *rich_loc) final override
-  {
-    diagnostic_metadata m;
-    bool warned;
-    switch (m_reg->get_memory_space ())
-      {
-      default:
-	m.add_cwe (787);
-	warned = warning_meta (rich_loc, m, get_controlling_option (),
-			       "buffer overflow");
-	break;
-      case MEMSPACE_STACK:
-	m.add_cwe (121);
-	warned = warning_meta (rich_loc, m, get_controlling_option (),
-			       "stack-based buffer overflow");
-	break;
-      case MEMSPACE_HEAP:
-	m.add_cwe (122);
-	warned = warning_meta (rich_loc, m, get_controlling_option (),
-			       "heap-based buffer overflow");
-	break;
-      }
-
-    if (warned)
-      {
-	char num_bytes_past_buf[WIDE_INT_PRINT_BUFFER_SIZE];
-	print_dec (m_out_of_bounds_range.m_size_in_bytes,
-		   num_bytes_past_buf, UNSIGNED);
-	if (m_diag_arg)
-	  inform (rich_loc->get_loc (), "write is %s bytes past the end"
-					" of %qE", num_bytes_past_buf,
-						   m_diag_arg);
-	else
-	  inform (rich_loc->get_loc (), "write is %s bytes past the end"
-					"of the region",
-					num_bytes_past_buf);
-      }
-
-    return warned;
-  }
-
-  label_text describe_final_event (const evdesc::final_event &ev)
-  final override
-  {
-    byte_size_t start = m_out_of_bounds_range.get_start_byte_offset ();
-    byte_size_t end = m_out_of_bounds_range.get_last_byte_offset ();
-    char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
-    print_dec (start, start_buf, SIGNED);
-    char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
-    print_dec (end, end_buf, SIGNED);
-
-    if (start == end)
-      {
-	if (m_diag_arg)
-	  return ev.formatted_print ("out-of-bounds write at byte %s but %qE"
-				     " ends at byte %E", start_buf, m_diag_arg,
-							 m_byte_bound);
-	return ev.formatted_print ("out-of-bounds write at byte %s but region"
-				   " ends at byte %E", start_buf,
-						       m_byte_bound);
-      }
-    else
-      {
-	if (m_diag_arg)
-	  return ev.formatted_print ("out-of-bounds write from byte %s till"
-				     " byte %s but %qE ends at byte %E",
-				     start_buf, end_buf, m_diag_arg,
-				     m_byte_bound);
-	return ev.formatted_print ("out-of-bounds write from byte %s till"
-				   " byte %s but region ends at byte %E",
-				   start_buf, end_buf, m_byte_bound);
-      }
-  }
-};
-
-/* Concrete subclass to complain about buffer overreads.  */
-
-class buffer_overread : public past_the_end
-{
-public:
-  buffer_overread (const region *reg, tree diag_arg,
-		   byte_range range, tree byte_bound)
-  : past_the_end (reg, diag_arg, range, byte_bound)
-  {}
-
-  bool emit (rich_location *rich_loc) final override
-  {
-    diagnostic_metadata m;
-    m.add_cwe (126);
-    bool warned = warning_meta (rich_loc, m, get_controlling_option (),
-				"buffer overread");
-
-    if (warned)
-      {
-	char num_bytes_past_buf[WIDE_INT_PRINT_BUFFER_SIZE];
-	print_dec (m_out_of_bounds_range.m_size_in_bytes,
-		   num_bytes_past_buf, UNSIGNED);
-	if (m_diag_arg)
-	  inform (rich_loc->get_loc (), "read is %s bytes past the end"
-					" of %qE", num_bytes_past_buf,
-						    m_diag_arg);
-	else
-	  inform (rich_loc->get_loc (), "read is %s bytes past the end"
-					"of the region",
-					num_bytes_past_buf);
-      }
-
-    return warned;
-  }
-
-  label_text describe_final_event (const evdesc::final_event &ev)
-  final override
-  {
-    byte_size_t start = m_out_of_bounds_range.get_start_byte_offset ();
-    byte_size_t end = m_out_of_bounds_range.get_last_byte_offset ();
-    char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
-    print_dec (start, start_buf, SIGNED);
-    char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
-    print_dec (end, end_buf, SIGNED);
-
-    if (start == end)
-      {
-	if (m_diag_arg)
-	  return ev.formatted_print ("out-of-bounds read at byte %s but %qE"
-				     " ends at byte %E", start_buf, m_diag_arg,
-							 m_byte_bound);
-	return ev.formatted_print ("out-of-bounds read at byte %s but region"
-				   " ends at byte %E", start_buf,
-						       m_byte_bound);
-      }
-    else
-      {
-	if (m_diag_arg)
-	  return ev.formatted_print ("out-of-bounds read from byte %s till"
-				     " byte %s but %qE ends at byte %E",
-				     start_buf, end_buf, m_diag_arg,
-				     m_byte_bound);
-	return ev.formatted_print ("out-of-bounds read from byte %s till"
-				   " byte %s but region ends at byte %E",
-				   start_buf, end_buf, m_byte_bound);
-      }
-  }
-};
-
-/* Concrete subclass to complain about buffer underflows.  */
-
-class buffer_underflow : public out_of_bounds
-{
-public:
-  buffer_underflow (const region *reg, tree diag_arg, byte_range range)
-  : out_of_bounds (reg, diag_arg, range)
-  {}
-
-  bool emit (rich_location *rich_loc) final override
-  {
-    diagnostic_metadata m;
-    m.add_cwe (124);
-    return warning_meta (rich_loc, m, get_controlling_option (),
-			 "buffer underflow");
-  }
-
-  label_text describe_final_event (const evdesc::final_event &ev)
-  final override
-  {
-    byte_size_t start = m_out_of_bounds_range.get_start_byte_offset ();
-    byte_size_t end = m_out_of_bounds_range.get_last_byte_offset ();
-    char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
-    print_dec (start, start_buf, SIGNED);
-    char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
-    print_dec (end, end_buf, SIGNED);
-
-    if (start == end)
-      {
-	if (m_diag_arg)
-	  return ev.formatted_print ("out-of-bounds write at byte %s but %qE"
-				     " starts at byte 0", start_buf,
-							  m_diag_arg);
-	return ev.formatted_print ("out-of-bounds write at byte %s but region"
-				   " starts at byte 0", start_buf);
-      }
-    else
-      {
-	if (m_diag_arg)
-	  return ev.formatted_print ("out-of-bounds write from byte %s till"
-				     " byte %s but %qE starts at byte 0",
-				     start_buf, end_buf, m_diag_arg);
-	return ev.formatted_print ("out-of-bounds write from byte %s till"
-				   " byte %s but region starts at byte 0",
-				   start_buf, end_buf);;
-      }
-  }
-};
-
-/* Concrete subclass to complain about buffer underreads.  */
-
-class buffer_underread : public out_of_bounds
-{
-public:
-  buffer_underread (const region *reg, tree diag_arg, byte_range range)
-  : out_of_bounds (reg, diag_arg, range)
-  {}
-
-  bool emit (rich_location *rich_loc) final override
-  {
-    diagnostic_metadata m;
-    m.add_cwe (127);
-    return warning_meta (rich_loc, m, get_controlling_option (),
-			 "buffer underread");
-  }
-
-  label_text describe_final_event (const evdesc::final_event &ev)
-  final override
-  {
-    byte_size_t start = m_out_of_bounds_range.get_start_byte_offset ();
-    byte_size_t end = m_out_of_bounds_range.get_last_byte_offset ();
-    char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
-    print_dec (start, start_buf, SIGNED);
-    char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
-    print_dec (end, end_buf, SIGNED);
-
-    if (start == end)
-      {
-	if (m_diag_arg)
-	  return ev.formatted_print ("out-of-bounds read at byte %s but %qE"
-				     " starts at byte 0", start_buf,
-							  m_diag_arg);
-	return ev.formatted_print ("out-of-bounds read at byte %s but region"
-				  " starts at byte 0", start_buf);
-      }
-    else
-      {
-	if (m_diag_arg)
-	  return ev.formatted_print ("out-of-bounds read from byte %s till"
-				     " byte %s but %qE starts at byte 0",
-				     start_buf, end_buf, m_diag_arg);
-	return ev.formatted_print ("out-of-bounds read from byte %s till"
-				   " byte %s but region starts at byte 0",
-				   start_buf, end_buf);;
-      }
-  }
-};
-
-/* Abstract class to complain about out-of-bounds read/writes where
-   the values are symbolic.  */
-
-class symbolic_past_the_end
-  : public pending_diagnostic_subclass<symbolic_past_the_end>
-{
-public:
-  symbolic_past_the_end (const region *reg, tree diag_arg, tree offset,
-			 tree num_bytes, tree capacity)
-  : m_reg (reg), m_diag_arg (diag_arg), m_offset (offset),
-    m_num_bytes (num_bytes), m_capacity (capacity)
-  {}
-
-  const char *get_kind () const final override
-  {
-    return "symbolic_past_the_end";
-  }
-
-  bool operator== (const symbolic_past_the_end &other) const
-  {
-    return m_reg == other.m_reg
-	   && pending_diagnostic::same_tree_p (m_diag_arg, other.m_diag_arg)
-	   && pending_diagnostic::same_tree_p (m_offset, other.m_offset)
-	   && pending_diagnostic::same_tree_p (m_num_bytes, other.m_num_bytes)
-	   && pending_diagnostic::same_tree_p (m_capacity, other.m_capacity);
-  }
-
-  int get_controlling_option () const final override
-  {
-    return OPT_Wanalyzer_out_of_bounds;
-  }
-
-  void mark_interesting_stuff (interesting_t *interest) final override
-  {
-    interest->add_region_creation (m_reg);
-  }
-
-  label_text
-  describe_region_creation_event (const evdesc::region_creation &ev) final
-  override
-  {
-    if (m_capacity)
-      return ev.formatted_print ("capacity is %qE bytes", m_capacity);
-
-    return label_text ();
-  }
-
-  label_text
-  describe_final_event (const evdesc::final_event &ev) final override
-  {
-    const char *byte_str;
-    if (pending_diagnostic::same_tree_p (m_num_bytes, integer_one_node))
-      byte_str = "byte";
-    else
-      byte_str = "bytes";
-
-    if (m_offset)
-      {
-	if (m_num_bytes && TREE_CODE (m_num_bytes) == INTEGER_CST)
-	  {
-	    if (m_diag_arg)
-	      return ev.formatted_print ("%s of %E %s at offset %qE"
-					 " exceeds %qE", m_dir_str,
-					 m_num_bytes, byte_str,
-					 m_offset, m_diag_arg);
-	    else
-	      return ev.formatted_print ("%s of %E %s at offset %qE"
-					 " exceeds the buffer", m_dir_str,
-					 m_num_bytes, byte_str, m_offset);
-	  }
-	else if (m_num_bytes)
-	  {
-	    if (m_diag_arg)
-	      return ev.formatted_print ("%s of %qE %s at offset %qE"
-					 " exceeds %qE", m_dir_str,
-					 m_num_bytes, byte_str,
-					 m_offset, m_diag_arg);
-	    else
-	      return ev.formatted_print ("%s of %qE %s at offset %qE"
-					 " exceeds the buffer", m_dir_str,
-					 m_num_bytes, byte_str, m_offset);
-	  }
-	else
-	  {
-	    if (m_diag_arg)
-	      return ev.formatted_print ("%s at offset %qE exceeds %qE",
-					 m_dir_str, m_offset, m_diag_arg);
-	    else
-	      return ev.formatted_print ("%s at offset %qE exceeds the"
-					 " buffer", m_dir_str, m_offset);
-	  }
-      }
-    if (m_diag_arg)
-      return ev.formatted_print ("out-of-bounds %s on %qE",
-				 m_dir_str, m_diag_arg);
-    return ev.formatted_print ("out-of-bounds %s", m_dir_str);
-  }
-
-protected:
-  const region *m_reg;
-  tree m_diag_arg;
-  tree m_offset;
-  tree m_num_bytes;
-  tree m_capacity;
-  const char *m_dir_str;
-};
-
-/* Concrete subclass to complain about overflows with symbolic values.  */
-
-class symbolic_buffer_overflow : public symbolic_past_the_end
-{
-public:
-  symbolic_buffer_overflow (const region *reg, tree diag_arg, tree offset,
-			    tree num_bytes, tree capacity)
-  : symbolic_past_the_end (reg, diag_arg, offset, num_bytes, capacity)
-  {
-    m_dir_str = "write";
-  }
-
-  bool emit (rich_location *rich_loc) final override
-  {
-    diagnostic_metadata m;
-    switch (m_reg->get_memory_space ())
-      {
-      default:
-	m.add_cwe (787);
-	return warning_meta (rich_loc, m, get_controlling_option (),
-			     "buffer overflow");
-      case MEMSPACE_STACK:
-	m.add_cwe (121);
-	return warning_meta (rich_loc, m, get_controlling_option (),
-			     "stack-based buffer overflow");
-      case MEMSPACE_HEAP:
-	m.add_cwe (122);
-	return warning_meta (rich_loc, m, get_controlling_option (),
-			     "heap-based buffer overflow");
-      }
-  }
-};
-
-/* Concrete subclass to complain about overreads with symbolic values.  */
-
-class symbolic_buffer_overread : public symbolic_past_the_end
-{
-public:
-  symbolic_buffer_overread (const region *reg, tree diag_arg, tree offset,
-			    tree num_bytes, tree capacity)
-  : symbolic_past_the_end (reg, diag_arg, offset, num_bytes, capacity)
-  {
-    m_dir_str = "read";
-  }
-
-  bool emit (rich_location *rich_loc) final override
-  {
-    diagnostic_metadata m;
-    m.add_cwe (126);
-    return warning_meta (rich_loc, m, get_controlling_option (),
-			 "buffer overread");
-  }
-};
-
-/* Check whether an access is past the end of the BASE_REG.  */
-
-void
-region_model::check_symbolic_bounds (const region *base_reg,
-				     const svalue *sym_byte_offset,
-				     const svalue *num_bytes_sval,
-				     const svalue *capacity,
-				     enum access_direction dir,
-				     region_model_context *ctxt) const
-{
-  gcc_assert (ctxt);
-
-  const svalue *next_byte
-    = m_mgr->get_or_create_binop (num_bytes_sval->get_type (), PLUS_EXPR,
-				  sym_byte_offset, num_bytes_sval);
-
-  if (eval_condition (next_byte, GT_EXPR, capacity).is_true ())
-    {
-      tree diag_arg = get_representative_tree (base_reg);
-      tree offset_tree = get_representative_tree (sym_byte_offset);
-      tree num_bytes_tree = get_representative_tree (num_bytes_sval);
-      tree capacity_tree = get_representative_tree (capacity);
-      switch (dir)
-	{
-	default:
-	  gcc_unreachable ();
-	  break;
-	case DIR_READ:
-	  ctxt->warn (make_unique<symbolic_buffer_overread> (base_reg,
-							     diag_arg,
-							     offset_tree,
-							     num_bytes_tree,
-							     capacity_tree));
-	  break;
-	case DIR_WRITE:
-	  ctxt->warn (make_unique<symbolic_buffer_overflow> (base_reg,
-							     diag_arg,
-							     offset_tree,
-							     num_bytes_tree,
-							     capacity_tree));
-	  break;
-	}
-    }
-}
-
-static tree
-maybe_get_integer_cst_tree (const svalue *sval)
-{
-  tree cst_tree = sval->maybe_get_constant ();
-  if (cst_tree && TREE_CODE (cst_tree) == INTEGER_CST)
-    return cst_tree;
-
-  return NULL_TREE;
-}
-
-/* May complain when the access on REG is out-of-bounds.  */
-
-void
-region_model::check_region_bounds (const region *reg,
-				   enum access_direction dir,
-				   region_model_context *ctxt) const
-{
-  gcc_assert (ctxt);
-
-  /* Get the offset.  */
-  region_offset reg_offset = reg->get_offset (m_mgr);
-  const region *base_reg = reg_offset.get_base_region ();
-
-  /* Bail out on symbolic regions.
-     (e.g. because the analyzer did not see previous offsets on the latter,
-     it might think that a negative access is before the buffer).  */
-  if (base_reg->symbolic_p ())
-    return;
-
-  /* Find out how many bytes were accessed.  */
-  const svalue *num_bytes_sval = reg->get_byte_size_sval (m_mgr);
-  tree num_bytes_tree = maybe_get_integer_cst_tree (num_bytes_sval);
-  /* Bail out if 0 bytes are accessed.  */
-  if (num_bytes_tree && zerop (num_bytes_tree))
-    return;
-
-  /* Get the capacity of the buffer.  */
-  const svalue *capacity = get_capacity (base_reg);
-  tree cst_capacity_tree = maybe_get_integer_cst_tree (capacity);
-
-  /* The constant offset from a pointer is represented internally as a sizetype
-     but should be interpreted as a signed value here.  The statement below
-     converts the offset from bits to bytes and then to a signed integer with
-     the same precision the sizetype has on the target system.
-
-     For example, this is needed for out-of-bounds-3.c test1 to pass when
-     compiled with a 64-bit gcc build targeting 32-bit systems.  */
-  byte_offset_t offset;
-  if (!reg_offset.symbolic_p ())
-    offset = wi::sext (reg_offset.get_bit_offset () >> LOG2_BITS_PER_UNIT,
-		       TYPE_PRECISION (size_type_node));
-
-  /* If either the offset or the number of bytes accessed are symbolic,
-     we have to reason about symbolic values.  */
-  if (reg_offset.symbolic_p () || !num_bytes_tree)
-    {
-      const svalue* byte_offset_sval;
-      if (!reg_offset.symbolic_p ())
-	{
-	  tree offset_tree = wide_int_to_tree (integer_type_node, offset);
-	  byte_offset_sval
-	    = m_mgr->get_or_create_constant_svalue (offset_tree);
-	}
-      else
-	byte_offset_sval = reg_offset.get_symbolic_byte_offset ();
-      check_symbolic_bounds (base_reg, byte_offset_sval, num_bytes_sval,
-			     capacity, dir, ctxt);
-      return;
-    }
-
-  /* Otherwise continue to check with concrete values.  */
-  byte_range out (0, 0);
-  /* NUM_BYTES_TREE should always be interpreted as unsigned.  */
-  byte_offset_t num_bytes_unsigned = wi::to_offset (num_bytes_tree);
-  byte_range read_bytes (offset, num_bytes_unsigned);
-  /* If read_bytes has a subset < 0, we do have an underflow.  */
-  if (read_bytes.falls_short_of_p (0, &out))
-    {
-      tree diag_arg = get_representative_tree (base_reg);
-      switch (dir)
-	{
-	default:
-	  gcc_unreachable ();
-	  break;
-	case DIR_READ:
-	  ctxt->warn (make_unique<buffer_underread> (reg, diag_arg, out));
-	  break;
-	case DIR_WRITE:
-	  ctxt->warn (make_unique<buffer_underflow> (reg, diag_arg, out));
-	  break;
-	}
-    }
-
-  /* For accesses past the end, we do need a concrete capacity.  No need to
-     do a symbolic check here because the inequality check does not reason
-     whether constants are greater than symbolic values.  */
-  if (!cst_capacity_tree)
-    return;
-
-  byte_range buffer (0, wi::to_offset (cst_capacity_tree));
-  /* If READ_BYTES exceeds BUFFER, we do have an overflow.  */
-  if (read_bytes.exceeds_p (buffer, &out))
-    {
-      tree byte_bound = wide_int_to_tree (size_type_node,
-					  buffer.get_next_byte_offset ());
-      tree diag_arg = get_representative_tree (base_reg);
-
-      switch (dir)
-	{
-	default:
-	  gcc_unreachable ();
-	  break;
-	case DIR_READ:
-	  ctxt->warn (make_unique<buffer_overread> (reg, diag_arg,
-						    out, byte_bound));
-	  break;
-	case DIR_WRITE:
-	  ctxt->warn (make_unique<buffer_overflow> (reg, diag_arg,
-						    out, byte_bound));
-	  break;
-	}
-    }
-}
-
 /* Ensure that all arguments at the call described by CD are checked
    for poisoned values, by calling get_rvalue on each argument.  */
 
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 86c42a24ff2..86bfb4ff8a0 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -606,6 +606,8 @@  private:
 			      region_model_context *ctxt) const;
   void check_region_size (const region *lhs_reg, const svalue *rhs_sval,
 			  region_model_context *ctxt) const;
+
+  /* Implemented in bounds-checking.cc  */
   void check_symbolic_bounds (const region *base_reg,
 			      const svalue *sym_byte_offset,
 			      const svalue *num_bytes_sval,