]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/gimple-array-bounds.cc
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / gimple-array-bounds.cc
index 352d0745178f97502723469746322f548bef5407..0517e5ddd8e6370a094b6ac6aa4283c91c1b9cca 100644 (file)
@@ -1,5 +1,5 @@
 /* Array bounds checking.
-   Copyright (C) 2005-2020 Free Software Foundation, Inc.
+   Copyright (C) 2005-2021 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -36,14 +36,131 @@ along with GCC; see the file COPYING3.  If not see
 #include "vr-values.h"
 #include "domwalk.h"
 #include "tree-cfg.h"
+#include "attribs.h"
+#include "pointer-query.h"
 
 // This purposely returns a value_range, not a value_range_equiv, to
 // break the dependency on equivalences for this pass.
 
 const value_range *
-array_bounds_checker::get_value_range (const_tree op)
+array_bounds_checker::get_value_range (const_tree op, gimple *stmt)
 {
-  return ranges->get_value_range (op);
+  return ranges->get_value_range (op, stmt);
+}
+
+/* Try to determine the DECL that REF refers to.  Return the DECL or
+   the expression closest to it.  Used in informational notes pointing
+   to referenced objects or function parameters.  */
+
+static tree
+get_base_decl (tree ref)
+{
+  tree base = get_base_address (ref);
+  if (DECL_P (base))
+    return base;
+
+  if (TREE_CODE (base) == MEM_REF)
+    base = TREE_OPERAND (base, 0);
+
+  if (TREE_CODE (base) != SSA_NAME)
+    return base;
+
+  do
+    {
+      gimple *def = SSA_NAME_DEF_STMT (base);
+      if (gimple_assign_single_p (def))
+       {
+         base = gimple_assign_rhs1 (def);
+         if (TREE_CODE (base) != ASSERT_EXPR)
+           return base;
+
+         base = TREE_OPERAND (base, 0);
+         if (TREE_CODE (base) != SSA_NAME)
+           return base;
+
+         continue;
+       }
+
+      if (!gimple_nop_p (def))
+       return base;
+
+      break;
+    } while (true);
+
+  tree var = SSA_NAME_VAR (base);
+  if (TREE_CODE (var) != PARM_DECL)
+    return base;
+
+  return var;
+}
+
+/* Return the constant byte size of the object or type referenced by
+   the MEM_REF ARG.  On success, set *PREF to the DECL or expression
+   ARG refers to.  Otherwise return null.  */
+
+static tree
+get_ref_size (tree arg, tree *pref)
+{
+  if (TREE_CODE (arg) != MEM_REF)
+    return NULL_TREE;
+
+  arg = TREE_OPERAND (arg, 0);
+  tree type = TREE_TYPE (arg);
+  if (!POINTER_TYPE_P (type))
+    return NULL_TREE;
+
+  type = TREE_TYPE (type);
+  if (TREE_CODE (type) != ARRAY_TYPE)
+    return NULL_TREE;
+
+  tree nbytes = TYPE_SIZE_UNIT (type);
+  if (!nbytes || TREE_CODE (nbytes) != INTEGER_CST)
+    return NULL_TREE;
+
+  *pref = get_base_decl (arg);
+  return nbytes;
+}
+
+/* Return true if REF is (likely) an ARRAY_REF to a trailing array member
+   of a struct.  It refines array_at_struct_end_p by detecting a pointer
+   to an array and an array parameter declared using the [N] syntax (as
+   opposed to a pointer) and returning false.  Set *PREF to the decl or
+   expression REF refers to.  */
+
+static bool
+trailing_array (tree arg, tree *pref)
+{
+  tree ref = arg;
+  tree base = get_base_decl (arg);
+  while (TREE_CODE (ref) == ARRAY_REF || TREE_CODE (ref) == MEM_REF)
+    ref = TREE_OPERAND (ref, 0);
+
+  if (TREE_CODE (ref) == COMPONENT_REF)
+    {
+      *pref = TREE_OPERAND (ref, 1);
+      tree type = TREE_TYPE (*pref);
+      if (TREE_CODE (type) == ARRAY_TYPE)
+       {
+         /* A multidimensional trailing array is not considered special
+            no matter what its major bound is.  */
+         type = TREE_TYPE (type);
+         if (TREE_CODE (type) == ARRAY_TYPE)
+           return false;
+       }
+    }
+  else
+    *pref = base;
+
+  tree basetype = TREE_TYPE (base);
+  if (TREE_CODE (base) == PARM_DECL
+      && POINTER_TYPE_P (basetype))
+    {
+      tree ptype = TREE_TYPE (basetype);
+      if (TREE_CODE (ptype) == ARRAY_TYPE)
+       return false;
+    }
+
+  return array_at_struct_end_p (arg);
 }
 
 /* Checks one ARRAY_REF in REF, located at LOCUS. Ignores flexible
@@ -51,14 +168,17 @@ array_bounds_checker::get_value_range (const_tree op)
    subscript is a constant, check if it is outside valid range.  If
    the array subscript is a RANGE, warn if it is non-overlapping with
    valid range.  IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside
-   a ADDR_EXPR.  Returns true if a warning has been issued.  */
+   a ADDR_EXPR.  Return  true if a warning has been issued or if
+   no-warning is set.  */
 
 bool
 array_bounds_checker::check_array_ref (location_t location, tree ref,
-                                      bool ignore_off_by_one)
+                                      gimple *stmt, bool ignore_off_by_one)
 {
-  if (TREE_NO_WARNING (ref))
-    return false;
+  if (warning_suppressed_p (ref, OPT_Warray_bounds))
+    /* Return true to have the caller prevent warnings for enclosing
+       refs.  */
+    return true;
 
   tree low_sub = TREE_OPERAND (ref, 1);
   tree up_sub = low_sub;
@@ -68,14 +188,13 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
   tree decl = NULL_TREE;
 
   /* Set for accesses to interior zero-length arrays.  */
-  bool interior_zero_len = false;
+  special_array_member sam{ };
 
   tree up_bound_p1;
 
   if (!up_bound
       || TREE_CODE (up_bound) != INTEGER_CST
-      || (warn_array_bounds < 2
-         && array_at_struct_end_p (ref)))
+      || (warn_array_bounds < 2 && trailing_array (ref, &decl)))
     {
       /* Accesses to trailing arrays via pointers may access storage
         beyond the types array bounds.  For such arrays, or for flexible
@@ -101,7 +220,7 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
            {
              /* Try to determine the size of the trailing array from
                 its initializer (if it has one).  */
-             if (tree refsize = component_ref_size (arg, &interior_zero_len))
+             if (tree refsize = component_ref_size (arg, &sam))
                if (TREE_CODE (refsize) == INTEGER_CST)
                  maxbound = refsize;
            }
@@ -116,7 +235,14 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
              poly_int64 off;
              if (tree base = get_addr_base_and_unit_offset (arg, &off))
                {
-                 if (!compref && DECL_P (base))
+                 if (TREE_CODE (base) == MEM_REF)
+                   {
+                     /* Try to determine the size from a pointer to
+                        an array if BASE is one.  */
+                     if (tree size = get_ref_size (base, &decl))
+                       maxbound = size;
+                   }
+                 else if (!compref && DECL_P (base))
                    if (tree basesize = DECL_SIZE_UNIT (base))
                      if (TREE_CODE (basesize) == INTEGER_CST)
                        {
@@ -161,7 +287,7 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
   const value_range *vr = NULL;
   if (TREE_CODE (low_sub) == SSA_NAME)
     {
-      vr = get_value_range (low_sub);
+      vr = get_value_range (low_sub, stmt);
       if (!vr->undefined_p () && !vr->varying_p ())
        {
          low_sub = vr->kind () == VR_RANGE ? vr->max () : vr->min ();
@@ -199,7 +325,7 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
                         "array subscript %E is below array bounds of %qT",
                         low_sub, artype);
 
-  if (!warned && interior_zero_len)
+  if (!warned && sam == special_array_member::int_0)
     warned = warning_at (location, OPT_Wzero_length_bounds,
                         (TREE_CODE (low_sub) == INTEGER_CST
                          ? G_("array subscript %E is outside the bounds "
@@ -217,7 +343,13 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
          fprintf (dump_file, "\n");
        }
 
-      ref = decl ? decl : TREE_OPERAND (ref, 0);
+      /* Avoid more warnings when checking more significant subscripts
+        of the same expression.  */
+      ref = TREE_OPERAND (ref, 0);
+      suppress_warning (ref, OPT_Warray_bounds);
+
+      if (decl)
+       ref = decl;
 
       tree rec = NULL_TREE;
       if (TREE_CODE (ref) == COMPONENT_REF)
@@ -235,13 +367,36 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
        inform (DECL_SOURCE_LOCATION (ref), "while referencing %qD", ref);
       if (rec && DECL_P (rec))
        inform (DECL_SOURCE_LOCATION (rec), "defined here %qD", rec);
-
-      TREE_NO_WARNING (ref) = 1;
     }
 
   return warned;
 }
 
+/* Wrapper around build_array_type_nelts that makes sure the array
+   can be created at all and handles zero sized arrays specially.  */
+
+static tree
+build_printable_array_type (tree eltype, unsigned HOST_WIDE_INT nelts)
+{
+  if (TYPE_SIZE_UNIT (eltype)
+      && TREE_CODE (TYPE_SIZE_UNIT (eltype)) == INTEGER_CST
+      && !integer_zerop (TYPE_SIZE_UNIT (eltype))
+      && TYPE_ALIGN_UNIT (eltype) > 1
+      && wi::zext (wi::to_wide (TYPE_SIZE_UNIT (eltype)),
+                  ffs_hwi (TYPE_ALIGN_UNIT (eltype)) - 1) != 0)
+    eltype = TYPE_MAIN_VARIANT (eltype);
+
+  if (nelts)
+    return build_array_type_nelts (eltype, nelts);
+
+  tree idxtype = build_range_type (sizetype, size_zero_node, NULL_TREE);
+  tree arrtype = build_array_type (eltype, idxtype);
+  arrtype = build_distinct_type_copy (TYPE_MAIN_VARIANT (arrtype));
+  TYPE_SIZE (arrtype) = bitsize_zero_node;
+  TYPE_SIZE_UNIT (arrtype) = size_zero_node;
+  return arrtype;
+}
+
 /* Checks one MEM_REF in REF, located at LOCATION, for out-of-bounds
    references to string constants.  If VRP can determine that the array
    subscript is a constant, check if it is outside valid range.
@@ -256,223 +411,105 @@ bool
 array_bounds_checker::check_mem_ref (location_t location, tree ref,
                                     bool ignore_off_by_one)
 {
-  if (TREE_NO_WARNING (ref))
+  if (warning_suppressed_p (ref, OPT_Warray_bounds))
     return false;
 
-  tree arg = TREE_OPERAND (ref, 0);
-  /* The constant and variable offset of the reference.  */
-  tree cstoff = TREE_OPERAND (ref, 1);
-  tree varoff = NULL_TREE;
-
-  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
-
-  /* The array or string constant bounds in bytes.  Initially set
-     to [-MAXOBJSIZE - 1, MAXOBJSIZE]  until a tighter bound is
-     determined.  */
-  offset_int arrbounds[2] = { -maxobjsize - 1, maxobjsize };
-
-  /* The minimum and maximum intermediate offset.  For a reference
-     to be valid, not only does the final offset/subscript must be
-     in bounds but all intermediate offsets should be as well.
-     GCC may be able to deal gracefully with such out-of-bounds
-     offsets so the checking is only enbaled at -Warray-bounds=2
-     where it may help detect bugs in uses of the intermediate
-     offsets that could otherwise not be detectable.  */
-  offset_int ioff = wi::to_offset (fold_convert (ptrdiff_type_node, cstoff));
-  offset_int extrema[2] = { 0, wi::abs (ioff) };
-
-  /* The range of the byte offset into the reference.  */
-  offset_int offrange[2] = { 0, 0 };
-
-  const value_range *vr = NULL;
-
-  /* Determine the offsets and increment OFFRANGE for the bounds of each.
-     The loop computes the range of the final offset for expressions such
-     as (A + i0 + ... + iN)[CSTOFF] where i0 through iN are SSA_NAMEs in
-     some range.  */
-  const unsigned limit = param_ssa_name_def_chain_limit;
-  for (unsigned n = 0; TREE_CODE (arg) == SSA_NAME && n < limit; ++n)
-    {
-      gimple *def = SSA_NAME_DEF_STMT (arg);
-      if (!is_gimple_assign (def))
-       break;
-
-      tree_code code = gimple_assign_rhs_code (def);
-      if (code == POINTER_PLUS_EXPR)
-       {
-         arg = gimple_assign_rhs1 (def);
-         varoff = gimple_assign_rhs2 (def);
-       }
-      else if (code == ASSERT_EXPR)
-       {
-         arg = TREE_OPERAND (gimple_assign_rhs1 (def), 0);
-         continue;
-       }
-      else
-       return false;
-
-      /* VAROFF should always be a SSA_NAME here (and not even
-        INTEGER_CST) but there's no point in taking chances.  */
-      if (TREE_CODE (varoff) != SSA_NAME)
-       break;
-
-      vr = get_value_range (varoff);
-      if (!vr || vr->undefined_p () || vr->varying_p ())
-       break;
-
-      if (!vr->constant_p ())
-       break;
-
-      if (vr->kind () == VR_RANGE)
-       {
-         offset_int min
-           = wi::to_offset (fold_convert (ptrdiff_type_node, vr->min ()));
-         offset_int max
-           = wi::to_offset (fold_convert (ptrdiff_type_node, vr->max ()));
-         if (min < max)
-           {
-             offrange[0] += min;
-             offrange[1] += max;
-           }
-         else
-           {
-             /* When MIN >= MAX, the offset is effectively in a union
-                of two ranges: [-MAXOBJSIZE -1, MAX] and [MIN, MAXOBJSIZE].
-                Since there is no way to represent such a range across
-                additions, conservatively add [-MAXOBJSIZE -1, MAXOBJSIZE]
-                to OFFRANGE.  */
-             offrange[0] += arrbounds[0];
-             offrange[1] += arrbounds[1];
-           }
-       }
-      else
-       {
-         /* For an anti-range, analogously to the above, conservatively
-            add [-MAXOBJSIZE -1, MAXOBJSIZE] to OFFRANGE.  */
-         offrange[0] += arrbounds[0];
-         offrange[1] += arrbounds[1];
-       }
-
-      /* Keep track of the minimum and maximum offset.  */
-      if (offrange[1] < 0 && offrange[1] < extrema[0])
-       extrema[0] = offrange[1];
-      if (offrange[0] > 0 && offrange[0] > extrema[1])
-       extrema[1] = offrange[0];
-
-      if (offrange[0] < arrbounds[0])
-       offrange[0] = arrbounds[0];
-
-      if (offrange[1] > arrbounds[1])
-       offrange[1] = arrbounds[1];
-    }
-
-  if (TREE_CODE (arg) == ADDR_EXPR)
-    {
-      arg = TREE_OPERAND (arg, 0);
-      if (TREE_CODE (arg) != STRING_CST
-         && TREE_CODE (arg) != PARM_DECL
-         && TREE_CODE (arg) != VAR_DECL)
-       return false;
-    }
-  else
-    return false;
-
-  /* The type of the object being referred to.  It can be an array,
-     string literal, or a non-array type when the MEM_REF represents
-     a reference/subscript via a pointer to an object that is not
-     an element of an array.  Incomplete types are excluded as well
-     because their size is not known.  */
-  tree reftype = TREE_TYPE (arg);
-  if (POINTER_TYPE_P (reftype)
-      || !COMPLETE_TYPE_P (reftype)
-      || TREE_CODE (TYPE_SIZE_UNIT (reftype)) != INTEGER_CST)
+  /* The statement used to allocate the array or null.  */
+  gimple *alloc_stmt = NULL;
+  /* For an allocation statement, the low bound of the size range.  */
+  offset_int minbound = 0;
+  /* The type and size of the access.  */
+  tree axstype = TREE_TYPE (ref);
+  offset_int axssize = 0;
+  if (tree access_size = TYPE_SIZE_UNIT (axstype))
+    if (TREE_CODE (access_size) == INTEGER_CST)
+      axssize = wi::to_offset (access_size);
+
+  access_ref aref;
+  if (!compute_objsize (ref, 0, &aref, ranges))
     return false;
 
-  /* Except in declared objects, references to trailing array members
-     of structs and union objects are excluded because MEM_REF doesn't
-     make it possible to identify the member where the reference
-     originated.  */
-  if (RECORD_OR_UNION_TYPE_P (reftype)
-      && (!VAR_P (arg)
-         || (DECL_EXTERNAL (arg) && array_at_struct_end_p (ref))))
+  if (aref.offset_in_range (axssize))
     return false;
 
-  arrbounds[0] = 0;
-
-  offset_int eltsize;
-  if (TREE_CODE (reftype) == ARRAY_TYPE)
+  if (TREE_CODE (aref.ref) == SSA_NAME)
     {
-      eltsize = wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (reftype)));
-      if (tree dom = TYPE_DOMAIN (reftype))
+      gimple *def = SSA_NAME_DEF_STMT (aref.ref);
+      if (is_gimple_call (def))
        {
-         tree bnds[] = { TYPE_MIN_VALUE (dom), TYPE_MAX_VALUE (dom) };
-         if (TREE_CODE (arg) == COMPONENT_REF)
-           {
-             offset_int size = maxobjsize;
-             if (tree fldsize = component_ref_size (arg))
-               size = wi::to_offset (fldsize);
-             arrbounds[1] = wi::lrshift (size, wi::floor_log2 (eltsize));
-           }
-         else if (array_at_struct_end_p (arg) || !bnds[0] || !bnds[1])
-           arrbounds[1] = wi::lrshift (maxobjsize, wi::floor_log2 (eltsize));
-         else
-           arrbounds[1] = (wi::to_offset (bnds[1]) - wi::to_offset (bnds[0])
-                           + 1) * eltsize;
+         /* Save the allocation call and the low bound on the size.  */
+         alloc_stmt = def;
+         minbound = aref.sizrng[0];
        }
-      else
-       arrbounds[1] = wi::lrshift (maxobjsize, wi::floor_log2 (eltsize));
-
-      /* Determine a tighter bound of the non-array element type.  */
-      tree eltype = TREE_TYPE (reftype);
-      while (TREE_CODE (eltype) == ARRAY_TYPE)
-       eltype = TREE_TYPE (eltype);
-      eltsize = wi::to_offset (TYPE_SIZE_UNIT (eltype));
     }
+                       
+  /* The range of the byte offset into the reference.  Adjusted below.  */
+  offset_int offrange[2] = { aref.offrng[0], aref.offrng[1] };
+
+  /* The type of the referenced object.  */
+  tree reftype = TREE_TYPE (aref.ref);
+  /* The size of the referenced array element.  */
+  offset_int eltsize = 1;
+  if (POINTER_TYPE_P (reftype))
+    reftype = TREE_TYPE (reftype);
+
+  if (TREE_CODE (reftype) == FUNCTION_TYPE)
+    /* Restore the original (pointer) type and avoid trying to create
+       an array of functions (done below).  */
+    reftype = TREE_TYPE (aref.ref);
   else
     {
-      eltsize = 1;
-      tree size = TYPE_SIZE_UNIT (reftype);
-      if (VAR_P (arg))
-       if (tree initsize = DECL_SIZE_UNIT (arg))
-         if (tree_int_cst_lt (size, initsize))
-           size = initsize;
-
-      arrbounds[1] = wi::to_offset (size);
+      /* The byte size of the array has already been determined above
+        based on a pointer ARG.  Set ELTSIZE to the size of the type
+        it points to and REFTYPE to the array with the size, rounded
+        down as necessary.  */
+      if (TREE_CODE (reftype) == ARRAY_TYPE)
+       reftype = TREE_TYPE (reftype);
+      if (tree refsize = TYPE_SIZE_UNIT (reftype))
+       if (TREE_CODE (refsize) == INTEGER_CST)
+         eltsize = wi::to_offset (refsize);
+
+      const offset_int nelts = aref.sizrng[1] / eltsize;
+      reftype = build_printable_array_type (reftype, nelts.to_uhwi ());
     }
 
-  offrange[0] += ioff;
-  offrange[1] += ioff;
-
   /* Compute the more permissive upper bound when IGNORE_OFF_BY_ONE
      is set (when taking the address of the one-past-last element
      of an array) but always use the stricter bound in diagnostics. */
-  offset_int ubound = arrbounds[1];
+  offset_int ubound = aref.sizrng[1];
   if (ignore_off_by_one)
-    ubound += 1;
-
-  if (arrbounds[0] == arrbounds[1]
-      || offrange[0] >= ubound
-      || offrange[1] < arrbounds[0])
+    ubound += eltsize;
+
+  /* Set if the lower bound of the subscript is out of bounds.  */
+  const bool lboob = (aref.sizrng[1] == 0
+                     || offrange[0] >= ubound
+                     || offrange[1] < 0);
+  /* Set if only the upper bound of the subscript is out of bounds.
+     This can happen when using a bigger type to index into an array
+     of a smaller type, as is common with unsigned char.  */
+  const bool uboob = !lboob && offrange[0] + axssize > ubound;
+  if (lboob || uboob)
     {
       /* Treat a reference to a non-array object as one to an array
         of a single element.  */
       if (TREE_CODE (reftype) != ARRAY_TYPE)
-       reftype = build_array_type_nelts (reftype, 1);
+       reftype = build_printable_array_type (reftype, 1);
 
       /* Extract the element type out of MEM_REF and use its size
         to compute the index to print in the diagnostic; arrays
         in MEM_REF don't mean anything.  A type with no size like
         void is as good as having a size of 1.  */
-      tree type = TREE_TYPE (ref);
-      while (TREE_CODE (type) == ARRAY_TYPE)
-       type = TREE_TYPE (type);
+      tree type = strip_array_types (TREE_TYPE (ref));
       if (tree size = TYPE_SIZE_UNIT (type))
        {
          offrange[0] = offrange[0] / wi::to_offset (size);
          offrange[1] = offrange[1] / wi::to_offset (size);
        }
+    }
 
-      bool warned;
+  bool warned = false;
+  if (lboob)
+    {
       if (offrange[0] == offrange[1])
        warned = warning_at (location, OPT_Warray_bounds,
                             "array subscript %wi is outside array bounds "
@@ -484,12 +521,28 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
                             "array bounds of %qT",
                             offrange[0].to_shwi (),
                             offrange[1].to_shwi (), reftype);
-      if (warned && DECL_P (arg))
-       inform (DECL_SOURCE_LOCATION (arg), "while referencing %qD", arg);
+    }
+  else if (uboob && !ignore_off_by_one)
+    {
+      tree backtype = reftype;
+      if (alloc_stmt)
+       /* If the memory was dynamically allocated refer to it as if
+          it were an untyped array of bytes.  */
+       backtype = build_array_type_nelts (unsigned_char_type_node,
+                                          aref.sizrng[1].to_uhwi ());
 
-      if (warned)
-       TREE_NO_WARNING (ref) = 1;
-      return warned;
+      warned = warning_at (location, OPT_Warray_bounds,
+                          "array subscript %<%T[%wi]%> is partly "
+                          "outside array bounds of %qT",
+                          axstype, offrange[0].to_shwi (), backtype);
+    }
+
+  if (warned)
+    {
+      /* TODO: Determine the access from the statement and use it.  */
+      aref.inform_access (access_none);
+      suppress_warning (ref, OPT_Warray_bounds);
+      return true;
     }
 
   if (warn_array_bounds < 2)
@@ -497,15 +550,15 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
 
   /* At level 2 check also intermediate offsets.  */
   int i = 0;
-  if (extrema[i] < -arrbounds[1] || extrema[i = 1] > ubound)
+  if (aref.offmax[i] < -aref.sizrng[1] || aref.offmax[i = 1] > ubound)
     {
-      HOST_WIDE_INT tmpidx = extrema[i].to_shwi () / eltsize.to_shwi ();
+      HOST_WIDE_INT tmpidx = aref.offmax[i].to_shwi () / eltsize.to_shwi ();
 
       if (warning_at (location, OPT_Warray_bounds,
                      "intermediate array offset %wi is outside array bounds "
                      "of %qT", tmpidx, reftype))
        {
-         TREE_NO_WARNING (ref) = 1;
+         suppress_warning (ref, OPT_Warray_bounds);
          return true;
        }
     }
@@ -517,19 +570,27 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
    address of an ARRAY_REF, and call check_array_ref on it.  */
 
 void
-array_bounds_checker::check_addr_expr (location_t location, tree t)
+array_bounds_checker::check_addr_expr (location_t location, tree t,
+                                      gimple *stmt)
 {
+  /* For the most significant subscript only, accept taking the address
+     of the just-past-the-end element.  */
+  bool ignore_off_by_one = true;
+
   /* Check each ARRAY_REF and MEM_REF in the reference chain. */
   do
     {
       bool warned = false;
       if (TREE_CODE (t) == ARRAY_REF)
-       warned = check_array_ref (location, t, true /*ignore_off_by_one*/);
+       {
+         warned = check_array_ref (location, t, stmt, ignore_off_by_one);
+         ignore_off_by_one = false;
+       }
       else if (TREE_CODE (t) == MEM_REF)
-       warned = check_mem_ref (location, t, true /*ignore_off_by_one*/);
+       warned = check_mem_ref (location, t, ignore_off_by_one);
 
       if (warned)
-       TREE_NO_WARNING (t) = true;
+       suppress_warning (t, OPT_Warray_bounds);
 
       t = TREE_OPERAND (t, 0);
     }
@@ -537,7 +598,7 @@ array_bounds_checker::check_addr_expr (location_t location, tree t)
 
   if (TREE_CODE (t) != MEM_REF
       || TREE_CODE (TREE_OPERAND (t, 0)) != ADDR_EXPR
-      || TREE_NO_WARNING (t))
+      || warning_suppressed_p (t, OPT_Warray_bounds))
     return;
 
   tree tem = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
@@ -597,10 +658,63 @@ array_bounds_checker::check_addr_expr (location_t location, tree t)
       if (DECL_P (t))
        inform (DECL_SOURCE_LOCATION (t), "while referencing %qD", t);
 
-      TREE_NO_WARNING (t) = 1;
+      suppress_warning (t, OPT_Warray_bounds);
     }
 }
 
+/* Return true if T is a reference to a member of a base class that's within
+   the bounds of the enclosing complete object.  The function "hacks" around
+   problems discussed in pr98266 and pr97595.  */
+
+static bool
+inbounds_memaccess_p (tree t)
+{
+  if (TREE_CODE (t) != COMPONENT_REF)
+    return false;
+
+  tree mref = TREE_OPERAND (t, 0);
+  if (TREE_CODE (mref) != MEM_REF)
+    return false;
+
+  /* Consider the access if its type is a derived class.  */
+  tree mreftype = TREE_TYPE (mref);
+  if (!RECORD_OR_UNION_TYPE_P (mreftype)
+      || !TYPE_BINFO (mreftype))
+    return false;
+
+  /* Compute the size of the referenced object (it could be dynamically
+     allocated).  */
+  access_ref aref;   // unused
+  tree refop = TREE_OPERAND (mref, 0);
+  tree refsize = compute_objsize (refop, 1, &aref);
+  if (!refsize || TREE_CODE (refsize) != INTEGER_CST)
+    return false;
+
+  /* Compute the byte offset of the member within its enclosing class.  */
+  tree fld = TREE_OPERAND (t, 1);
+  tree fldpos = byte_position (fld);
+  if (TREE_CODE (fldpos) != INTEGER_CST)
+    return false;
+
+  /* Compute the byte offset of the member with the outermost complete
+     object by adding its offset computed above to the MEM_REF offset.  */
+  tree refoff = TREE_OPERAND (mref, 1);
+  tree fldoff = int_const_binop (PLUS_EXPR, fldpos, refoff);
+  /* Return false if the member offset is greater or equal to the size
+     of the complete object.  */
+  if (!tree_int_cst_lt (fldoff, refsize))
+    return false;
+
+  tree fldsiz = DECL_SIZE_UNIT (fld);
+  if (!fldsiz || TREE_CODE (fldsiz) != INTEGER_CST)
+    return false;
+
+  /* Return true if the offset just past the end of the member is less
+     than or equal to the size of the complete object.  */
+  tree fldend = int_const_binop (PLUS_EXPR, fldoff, fldsiz);
+  return tree_int_cst_le (fldend, refsize);
+}
+
 /* Callback for walk_tree to check a tree for out of bounds array
    accesses.  The array_bounds_checker class is passed in DATA.  */
 
@@ -622,19 +736,26 @@ array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree,
   bool warned = false;
   array_bounds_checker *checker = (array_bounds_checker *) wi->info;
   if (TREE_CODE (t) == ARRAY_REF)
-    warned = checker->check_array_ref (location, t,
+    warned = checker->check_array_ref (location, t, wi->stmt,
                                       false/*ignore_off_by_one*/);
   else if (TREE_CODE (t) == MEM_REF)
     warned = checker->check_mem_ref (location, t,
                                     false /*ignore_off_by_one*/);
   else if (TREE_CODE (t) == ADDR_EXPR)
     {
-      checker->check_addr_expr (location, t);
-      *walk_subtree = FALSE;
+      checker->check_addr_expr (location, t, wi->stmt);
+      *walk_subtree = false;
     }
-  /* Propagate the no-warning bit to the outer expression.  */
+  else if (inbounds_memaccess_p (t))
+    /* Hack: Skip MEM_REF checks in accesses to a member of a base class
+       at an offset that's within the bounds of the enclosing object.
+       See pr98266 and pr97595.  */
+    *walk_subtree = false;
+
+  /* Propagate the no-warning bit to the outer statement to avoid also
+     issuing -Wstringop-overflow/-overread for the out-of-bounds accesses.  */
   if (warned)
-    TREE_NO_WARNING (t) = true;
+    suppress_warning (wi->stmt, OPT_Warray_bounds);
 
   return NULL_TREE;
 }