Generalize compute_objsize to return maximum size/offset instead of failing (PR middl...
authorMartin Sebor <msebor@redhat.com>
Fri, 9 Oct 2020 20:48:43 +0000 (14:48 -0600)
committerMartin Sebor <msebor@redhat.com>
Mon, 12 Oct 2020 15:05:55 +0000 (09:05 -0600)
Also resolves:
PR middle-end/97342 - bogus -Wstringop-overflow with nonzero signed and unsigned offsets
PR middle-end/97023 - missing warning on buffer overflow in chained mempcpy
PR middle-end/96384 - bogus -Wstringop-overflow= storing into multidimensional array with index in range

gcc/ChangeLog:

PR middle-end/97342
PR middle-end/97023
PR middle-end/96384
* builtins.c (access_ref::access_ref): Initialize new member.  Use
new enum.
(access_ref::size_remaining): Define new member function.
(inform_access): Handle expressions referencing objects.
(gimple_call_alloc_size): Call get_size_range instead of get_range.
(gimple_call_return_array): New function.
(get_range): Rename...
(get_offset_range): ...to this.  Improve detection of ranges from
types of expressions.
(gimple_call_return_array): Adjust calls to get_range per above.
(compute_objsize): Same.  Set maximum size or offset instead of
failing for unknown objects and handle more kinds of expressions.
(compute_objsize): Call access_ref::size_remaining.
(compute_objsize): Have transitional wrapper fail for pointers
into unknown objects.
(expand_builtin_strncmp): Call access_ref::size_remaining and
handle new cases.
* builtins.h (access_ref::size_remaining): Declare new member function.
(access_ref::set_max_size_range): Define new member function.
(access_ref::add_ofset, access_ref::add_max_ofset): Same.
(access_ref::add_base0): New data member.
* calls.c (get_size_range): Change argument type.  Handle new
condition.
* calls.h (get_size_range): Adjust signature.
(enum size_range_flags): Define new type.
* gimple-ssa-warn-restrict.c (builtin_memref::builtin_memref): Correct
argument to get_size_range.
* tree-ssa-strlen.c (get_range): Handle anti-ranges.
(maybe_warn_overflow): Check DECL_P before assuming it's one.

gcc/testsuite/ChangeLog:

PR middle-end/97342
PR middle-end/97023
PR middle-end/96384
* c-c++-common/Wrestrict.c: Adjust comment.
* gcc.dg/Wstringop-overflow-34.c: Remove xfail.
* gcc.dg/Wstringop-overflow-43.c: Remove xfails.  Adjust regex patterns.
* gcc.dg/pr51683.c: Prune out expected warning.
* gcc.target/i386/pr60693.c: Same.
* g++.dg/warn/Wplacement-new-size-8.C: New test.
* gcc.dg/Wstringop-overflow-41.c: New test.
* gcc.dg/Wstringop-overflow-44.s: New test.
* gcc.dg/Wstringop-overflow-45.c: New test.
* gcc.dg/Wstringop-overflow-46.c: New test.
* gcc.dg/Wstringop-overflow-47.c: New test.
* gcc.dg/Wstringop-overflow-49.c: New test.
* gcc.dg/Wstringop-overflow-50.c: New test.
* gcc.dg/Wstringop-overflow-51.c: New test.
* gcc.dg/Wstringop-overflow-52.c: New test.
* gcc.dg/Wstringop-overflow-53.c: New test.
* gcc.dg/Wstringop-overflow-54.c: New test.
* gcc.dg/Wstringop-overflow-55.c: New test.
* gcc.dg/Wstringop-overread-5.c: New test.

25 files changed:
gcc/builtins.c
gcc/builtins.h
gcc/calls.c
gcc/calls.h
gcc/gimple-ssa-warn-restrict.c
gcc/testsuite/c-c++-common/Wrestrict.c
gcc/testsuite/g++.dg/warn/Wplacement-new-size-8.C [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-34.c
gcc/testsuite/gcc.dg/Wstringop-overflow-41.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-43.c
gcc/testsuite/gcc.dg/Wstringop-overflow-44.s [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-45.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-46.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-47.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-49.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-50.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-51.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-52.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-53.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-54.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-55.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overread-5.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/pr51683.c
gcc/testsuite/gcc.target/i386/pr60693.c
gcc/tree-ssa-strlen.c

index 283c1e6674c8b612156a7ce7180f38a6b9e5f083..3a77da2b10ceeec04c731b3e670b305b93dc1d6f 100644 (file)
@@ -183,8 +183,6 @@ static void maybe_emit_chk_warning (tree, enum built_in_function);
 static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function);
 static void maybe_emit_free_warning (tree);
 static tree fold_builtin_object_size (tree, tree);
-static bool get_range (tree, gimple *, signop, offset_int[2],
-                      range_query * = NULL);
 static bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
 
 unsigned HOST_WIDE_INT target_newline;
@@ -200,7 +198,7 @@ static void expand_builtin_sync_synchronize (void);
 
 access_ref::access_ref (tree bound /* = NULL_TREE */,
                        bool minaccess /* = false */)
-: ref (), eval ([](tree x){ return x; }), trail1special (true)
+: ref (), eval ([](tree x){ return x; }), trail1special (true), base0 (true)
 {
   /* Set to valid.  */
   offrng[0] = offrng[1] = 0;
@@ -215,7 +213,7 @@ access_ref::access_ref (tree bound /* = NULL_TREE */,
      set the bounds of the access to reflect both it and MINACCESS.
      BNDRNG[0] is the size of the minimum access.  */
   tree rng[2];
-  if (bound && get_size_range (bound, rng, true))
+  if (bound && get_size_range (bound, rng, SR_ALLOW_ZERO))
     {
       bndrng[0] = wi::to_offset (rng[0]);
       bndrng[1] = wi::to_offset (rng[1]);
@@ -223,6 +221,131 @@ access_ref::access_ref (tree bound /* = NULL_TREE */,
     }
 }
 
+/* Return the maximum amount of space remaining and if non-null, set
+   argument to the minimum.  */
+
+offset_int
+access_ref::size_remaining (offset_int *pmin /* = NULL */) const
+{
+  offset_int minbuf;
+  if (!pmin)
+    pmin = &minbuf;
+
+  /* add_offset() ensures the offset range isn't inverted.  */
+  gcc_checking_assert (offrng[0] <= offrng[1]);
+
+  if (base0)
+    {
+      /* The offset into referenced object is zero-based (i.e., it's
+        not referenced by a pointer into middle of some unknown object).  */
+      if (offrng[0] < 0 && offrng[1] < 0)
+       {
+         /* If the offset is negative the remaining size is zero.  */
+         *pmin = 0;
+         return 0;
+       }
+
+      if (sizrng[1] <= offrng[0])
+       {
+         /* If the starting offset is greater than or equal to the upper
+            bound on the size of the object, the space remaining is zero.
+            As a special case, if it's equal, set *PMIN to -1 to let
+            the caller know the offset is valid and just past the end.  */
+         *pmin = sizrng[1] == offrng[0] ? -1 : 0;
+         return 0;
+       }
+
+      /* Otherwise return the size minus the lower bound of the offset.  */
+      offset_int or0 = offrng[0] < 0 ? 0 : offrng[0];
+
+      *pmin = sizrng[0] - or0;
+      return sizrng[1] - or0;
+    }
+
+  /* The offset to the referenced object isn't zero-based (i.e., it may
+     refer to a byte other than the first.  The size of such an object
+     is constrained only by the size of the address space (the result
+     of max_object_size()).  */
+  if (sizrng[1] <= offrng[0])
+    {
+      *pmin = 0;
+      return 0;
+    }
+
+  offset_int or0 = offrng[0] < 0 ? 0 : offrng[0];
+
+  *pmin = sizrng[0] - or0;
+  return sizrng[1] - or0;
+}
+
+/* Add the range [MIN, MAX] to the offset range.  For known objects (with
+   zero-based offsets) at least one of whose offset's bounds is in range,
+   constrain the other (or both) to the bounds of the object (i.e., zero
+   and the upper bound of its size).  This improves the quality of
+   diagnostics.  */
+
+void access_ref::add_offset (const offset_int &min, const offset_int &max)
+{
+  if (min <= max)
+    {
+      /* To add an ordinary range just add it to the bounds.  */
+      offrng[0] += min;
+      offrng[1] += max;
+    }
+  else if (!base0)
+    {
+      /* To add an inverted range to an offset to an unknown object
+        expand it to the maximum.  */
+      add_max_offset ();
+      return;
+    }
+  else
+    {
+      /* To add an inverted range to an offset to an known object set
+        the upper bound to the maximum representable offset value
+        (which may be greater than MAX_OBJECT_SIZE).
+        The lower bound is either the sum of the current offset and
+        MIN when abs(MAX) is greater than the former, or zero otherwise.
+        Zero because then then inverted range includes the negative of
+        the lower bound.  */
+      offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+      offrng[1] = maxoff;
+
+      if (max >= 0)
+       {
+         offrng[0] = 0;
+         return;
+       }
+
+      offrng[1] = maxoff;
+      offset_int absmax = wi::abs (max);
+      if (offrng[0] < absmax)
+       offrng[0] += min;
+      else
+       offrng[0] = 0;
+    }
+
+  if (!base0)
+    return;
+
+  /* When referencing a known object check to see if the offset computed
+     so far is in bounds... */
+  offset_int remrng[2];
+  remrng[1] = size_remaining (remrng);
+  if (remrng[1] > 0 || remrng[0] < 0)
+    {
+      /* ...if so, constrain it so that neither bound exceeds the size of
+        the object.  Out of bounds offsets are left unchanged, and, for
+        better or worse, become in bounds later.  They should be detected
+        and diagnosed at the point they first become invalid by
+        -Warray-bounds.  */
+      if (offrng[0] < 0)
+       offrng[0] = 0;
+      if (offrng[1] > sizrng[1])
+       offrng[1] = sizrng[1];
+    }
+}
+
 /* Return true if NAME starts with __builtin_ or __sync_.  */
 
 static bool
@@ -3757,15 +3880,24 @@ inform_access (const access_ref &ref, access_mode mode)
   if (!ref.ref)
     return;
 
-  /* Convert offset range and avoid including a zero range since it isn't
-     necessarily meaningful.  */
-  long long minoff = 0, maxoff = 0;
-  if (wi::fits_shwi_p (ref.offrng[0])
-      && wi::fits_shwi_p (ref.offrng[1]))
-    {
-      minoff = ref.offrng[0].to_shwi ();
-      maxoff = ref.offrng[1].to_shwi ();
-    }
+  /* Convert offset range and avoid including a zero range since it
+     isn't necessarily meaningful.  */
+  HOST_WIDE_INT diff_min = tree_to_shwi (TYPE_MIN_VALUE (ptrdiff_type_node));
+  HOST_WIDE_INT diff_max = tree_to_shwi (TYPE_MAX_VALUE (ptrdiff_type_node));
+  HOST_WIDE_INT minoff;
+  HOST_WIDE_INT maxoff = diff_max;
+  if (wi::fits_shwi_p (ref.offrng[0]))
+    minoff = ref.offrng[0].to_shwi ();
+  else
+    minoff = ref.offrng[0] < 0 ? diff_min : diff_max;
+
+  if (wi::fits_shwi_p (ref.offrng[1]))
+    maxoff = ref.offrng[1].to_shwi ();
+
+  if (maxoff <= diff_min || maxoff >= diff_max)
+    /* Avoid mentioning an upper bound that's equal to or in excess
+       of the maximum of ptrdiff_t.  */
+    maxoff = minoff;
 
   /* Convert size range and always include it since all sizes are
      meaningful. */
@@ -3799,23 +3931,27 @@ inform_access (const access_ref &ref, access_mode mode)
        sprintf (sizestr, "[%llu, %llu]", minsize, maxsize);
 
     }
-  else
+  else if (DECL_P (ref.ref))
     loc = DECL_SOURCE_LOCATION (ref.ref);
+  else if (EXPR_P (ref.ref) && EXPR_HAS_LOCATION (ref.ref))
+    loc = EXPR_LOCATION (ref.ref);
+  else
+    return;
 
   if (mode == access_read_write || mode == access_write_only)
     {
-      if (DECL_P (ref.ref))
+      if (allocfn == NULL_TREE)
        {
          if (minoff == maxoff)
            {
              if (minoff == 0)
-               inform (loc, "destination object %qD", ref.ref);
+               inform (loc, "destination object %qE", ref.ref);
              else
-               inform (loc, "at offset %lli into destination object %qD",
+               inform (loc, "at offset %wi into destination object %qE",
                        minoff, ref.ref);
            }
          else
-           inform (loc, "at offset [%lli, %lli] into destination object %qD",
+           inform (loc, "at offset [%wi, %wi] into destination object %qE",
                    minoff, maxoff, ref.ref);
          return;
        }
@@ -3827,12 +3963,12 @@ inform_access (const access_ref &ref, access_mode mode)
                    sizestr, allocfn);
          else
            inform (loc,
-                   "at offset %lli into destination object of size %s "
+                   "at offset %wi into destination object of size %s "
                    "allocated by %qE", minoff, sizestr, allocfn);
        }
       else
        inform (loc,
-               "at offset [%lli, %lli] into destination object of size %s "
+               "at offset [%wi, %wi] into destination object of size %s "
                "allocated by %qE",
                minoff, maxoff, sizestr, allocfn);
 
@@ -3846,11 +3982,11 @@ inform_access (const access_ref &ref, access_mode mode)
          if (minoff == 0)
            inform (loc, "source object %qD", ref.ref);
          else
-           inform (loc, "at offset %lli into source object %qD",
+           inform (loc, "at offset %wi into source object %qD",
                    minoff, ref.ref);
        }
       else
-       inform (loc, "at offset [%lli, %lli] into source object %qD",
+       inform (loc, "at offset [%wi, %wi] into source object %qD",
                minoff, maxoff, ref.ref);
       return;
     }
@@ -3862,12 +3998,12 @@ inform_access (const access_ref &ref, access_mode mode)
                sizestr, allocfn);
       else
        inform (loc,
-               "at offset %lli into source object of size %s "
+               "at offset %wi into source object of size %s "
                "allocated by %qE", minoff, sizestr, allocfn);
     }
   else
     inform (loc,
-           "at offset [%lli, %lli] into source object of size %s "
+           "at offset [%wi, %wi] into source object of size %s "
            "allocated by %qE",
            minoff, maxoff, sizestr, allocfn);
 }
@@ -4152,8 +4288,14 @@ check_access (tree exp, tree dstwrite,
                   && range[0]
                   && TREE_CODE (slen) == INTEGER_CST
                   && tree_int_cst_lt (slen, range[0]));
-
-  if (!overread && pad && pad->src.sizrng[1] >= 0 && pad->src.offrng[0] >= 0)
+  /* If none is determined try to get a better answer based on the details
+     in PAD.  */
+  if (!overread
+      && pad
+      && pad->src.sizrng[1] >= 0
+      && pad->src.offrng[0] >= 0
+      && (pad->src.offrng[1] < 0
+         || pad->src.offrng[0] <= pad->src.offrng[1]))
     {
       /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
         PAD is nonnull and BNDRNG is valid.  */
@@ -4215,7 +4357,7 @@ check_read_access (tree exp, tree src, tree bound /* = NULL_TREE */,
 
 tree
 gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
-                       range_query *rvals /* = NULL */)
+                       range_query * /* = NULL */)
 {
   if (!stmt)
     return NULL_TREE;
@@ -4267,14 +4409,17 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
   if (!rng1)
     rng1 = rng1_buf;
 
+  /* Use maximum precision to avoid overflow below.  */
   const int prec = ADDR_MAX_PRECISION;
-  const tree size_max = TYPE_MAX_VALUE (sizetype);
-  if (!get_range (size, stmt, rng1, rvals))
-    {
-      /* Use the full non-negative range on failure.  */
-      rng1[0] = wi::zero (prec);
-      rng1[1] = wi::to_wide (size_max, prec);
-    }
+
+  {
+    tree r[2];
+    /* Determine the largest valid range size, including zero.  */
+    if (!get_size_range (size, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
+      return NULL_TREE;
+    rng1[0] = wi::to_wide (r[0], prec);
+    rng1[1] = wi::to_wide (r[1], prec);
+  }
 
   if (argidx2 > nargs && TREE_CODE (size) == INTEGER_CST)
     return fold_convert (sizetype, size);
@@ -4283,26 +4428,24 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
      of the upper bounds as a constant.  Ignore anti-ranges.  */
   tree n = argidx2 < nargs ? gimple_call_arg (stmt, argidx2) : integer_one_node;
   wide_int rng2[2];
-         if (!get_range (n, stmt, rng2, rvals))
-    {
+  {
+    tree r[2];
       /* As above, use the full non-negative range on failure.  */
-      rng2[0] = wi::zero (prec);
-      rng2[1] = wi::to_wide (size_max, prec);
-    }
-
-  /* Extend to the maximum precision to avoid overflow.  */
-  rng1[0] = wide_int::from (rng1[0], prec, UNSIGNED);
-  rng1[1] = wide_int::from (rng1[1], prec, UNSIGNED);
-  rng2[0] = wide_int::from (rng2[0], prec, UNSIGNED);
-  rng2[1] = wide_int::from (rng2[1], prec, UNSIGNED);
+    if (!get_size_range (n, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
+      return NULL_TREE;
+    rng2[0] = wi::to_wide (r[0], prec);
+    rng2[1] = wi::to_wide (r[1], prec);
+  }
 
   /* Compute products of both bounds for the caller but return the lesser
      of SIZE_MAX and the product of the upper bounds as a constant.  */
   rng1[0] = rng1[0] * rng2[0];
   rng1[1] = rng1[1] * rng2[1];
+
+  const tree size_max = TYPE_MAX_VALUE (sizetype);
   if (wi::gtu_p (rng1[1], wi::to_wide (size_max, prec)))
     {
-      rng1[1] = wi::to_wide (size_max);
+      rng1[1] = wi::to_wide (size_max, prec);
       return size_max;
     }
 
@@ -4315,7 +4458,8 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
    Return the function parameter on success and null otherwise.  */
 
 tree
-gimple_parm_array_size (tree ptr, wide_int rng[2], range_query * /* = NULL */)
+gimple_parm_array_size (tree ptr, wide_int rng[2],
+                       range_query * /* = NULL */)
 {
   /* For a function argument try to determine the byte size of the array
      from the current function declaratation (e.g., attribute access or
@@ -4363,27 +4507,43 @@ gimple_parm_array_size (tree ptr, wide_int rng[2], range_query * /* = NULL */)
   return var;
 }
 
-/* Wrapper around the wide_int overload of get_range.  Returns the same
-   result but accepts offset_int instead.  */
+/* Wrapper around the wide_int overload of get_range that accepts
+   offset_int instead.  For middle end expressions returns the same
+   result.  For a subset of nonconstamt expressions emitted by the front
+   end determines a more precise range than would be possible otherwise.  */
 
 static bool
-get_range (tree x, gimple *stmt, signop sgn, offset_int r[2],
-          range_query *rvals /* = NULL */)
+get_offset_range (tree x, gimple *stmt, offset_int r[2], range_query *rvals)
 {
-  tree type = TREE_TYPE (x);
-  if (TREE_CODE (x) != INTEGER_CST
-      && TREE_CODE (x) != SSA_NAME)
+  offset_int add = 0;
+  if (TREE_CODE (x) == PLUS_EXPR)
     {
-      if (TYPE_UNSIGNED (type))
+      /* Handle constant offsets in pointer addition expressions seen
+        n the front end IL.  */
+      tree op = TREE_OPERAND (x, 1);
+      if (TREE_CODE (op) == INTEGER_CST)
        {
-         if (sgn == SIGNED)
-           type = signed_type_for (type);
+         op = fold_convert (signed_type_for (TREE_TYPE (op)), op);
+         add = wi::to_offset (op);
+         x = TREE_OPERAND (x, 0);
        }
-      else if (sgn == UNSIGNED)
-       type = unsigned_type_for (type);
+    }
+
+  if (TREE_CODE (x) == NOP_EXPR)
+    /* Also handle conversions to sizetype seen in the front end IL.  */
+    x = TREE_OPERAND (x, 0);
+
+  tree type = TREE_TYPE (x);
 
-      r[0] = wi::to_offset (TYPE_MIN_VALUE (type));
-      r[1] = wi::to_offset (TYPE_MAX_VALUE (type));
+   if (TREE_CODE (x) != INTEGER_CST
+      && TREE_CODE (x) != SSA_NAME)
+    {
+      if (TYPE_UNSIGNED (type)
+         && TYPE_PRECISION (type) == TYPE_PRECISION (sizetype))
+       type = signed_type_for (type);
+
+      r[0] = wi::to_offset (TYPE_MIN_VALUE (type)) + add;
+      r[1] = wi::to_offset (TYPE_MAX_VALUE (type)) + add;
       return x;
     }
 
@@ -4391,6 +4551,7 @@ get_range (tree x, gimple *stmt, signop sgn, offset_int r[2],
   if (!get_range (x, stmt, wr, rvals))
     return false;
 
+  signop sgn = SIGNED;
   /* Only convert signed integers or unsigned sizetype to a signed
      offset and avoid converting large positive values in narrower
      types to negative offsets.  */
@@ -4403,6 +4564,83 @@ get_range (tree x, gimple *stmt, signop sgn, offset_int r[2],
   return true;
 }
 
+/* Return the argument that the call STMT to a built-in function returns
+   or null if it doesn't.  On success, set OFFRNG[] to the range of offsets
+   from the argument reflected in the value returned by the built-in if it
+   can be determined, otherwise to 0 and HWI_M1U respectively.  */
+
+static tree
+gimple_call_return_array (gimple *stmt, offset_int offrng[2],
+                         range_query *rvals)
+{
+  if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
+      || gimple_call_num_args (stmt) < 1)
+    return NULL_TREE;
+
+  tree fn = gimple_call_fndecl (stmt);
+  switch (DECL_FUNCTION_CODE (fn))
+    {
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMMOVE_CHK:
+    case BUILT_IN_MEMSET:
+    case BUILT_IN_STPCPY:
+    case BUILT_IN_STPCPY_CHK:
+    case BUILT_IN_STPNCPY:
+    case BUILT_IN_STPNCPY_CHK:
+    case BUILT_IN_STRCAT:
+    case BUILT_IN_STRCAT_CHK:
+    case BUILT_IN_STRCPY:
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STRNCAT:
+    case BUILT_IN_STRNCAT_CHK:
+    case BUILT_IN_STRNCPY:
+    case BUILT_IN_STRNCPY_CHK:
+      offrng[0] = offrng[1] = 0;
+      return gimple_call_arg (stmt, 0);
+
+    case BUILT_IN_MEMPCPY:
+    case BUILT_IN_MEMPCPY_CHK:
+      {
+       tree off = gimple_call_arg (stmt, 2);
+       if (!get_offset_range (off, stmt, offrng, rvals))
+         {
+           offrng[0] = 0;
+           offrng[1] = HOST_WIDE_INT_M1U;
+         }
+       return gimple_call_arg (stmt, 0);
+      }
+
+    case BUILT_IN_MEMCHR:
+      {
+       tree off = gimple_call_arg (stmt, 2);
+       if (get_offset_range (off, stmt, offrng, rvals))
+         offrng[0] = 0;
+       else
+         {
+           offrng[0] = 0;
+           offrng[1] = HOST_WIDE_INT_M1U;
+         }
+       return gimple_call_arg (stmt, 0);
+      }
+
+    case BUILT_IN_STRCHR:
+    case BUILT_IN_STRRCHR:
+    case BUILT_IN_STRSTR:
+      {
+       offrng[0] = 0;
+       offrng[1] = HOST_WIDE_INT_M1U;
+      }
+      return gimple_call_arg (stmt, 0);
+
+    default:
+      break;
+    }
+
+  return NULL_TREE;
+}
+
 /* Helper to compute the size of the object referenced by the PTR
    expression which must have pointer type, using Object Size type
    OSTYPE (only the least significant 2 bits are used).
@@ -4412,7 +4650,8 @@ get_range (tree x, gimple *stmt, signop sgn, offset_int r[2],
    the object(s).
    VISITED is used to avoid visiting the same PHI operand multiple
    times, and, when nonnull, RVALS to determine range information.
-   Returns true on success, false when the size cannot be determined.
+   Returns true on success, false when a meaningful size (or range)
+   cannot be determined.
 
    The function is intended for diagnostics and should not be used
    to influence code generation or optimization.  */
@@ -4429,25 +4668,40 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 
   if (DECL_P (ptr))
     {
-      /* Bail if the reference is to the pointer itself (as opposed
-        to what it points to).  */
+      pref->ref = ptr;
+
       if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr)))
-       return false;
+       {
+         /* Set the maximum size if the reference is to the pointer
+            itself (as opposed to what it points to).  */
+         pref->set_max_size_range ();
+         return true;
+       }
 
-      pref->ref = ptr;
       if (tree size = decl_init_size (ptr, false))
        if (TREE_CODE (size) == INTEGER_CST)
          {
            pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
            return true;
          }
-      pref->sizrng[0] = 0;
-      pref->sizrng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+
+      pref->set_max_size_range ();
       return true;
     }
 
   const tree_code code = TREE_CODE (ptr);
 
+  if (code == BIT_FIELD_REF)
+    {
+      tree ref = TREE_OPERAND (ptr, 0);
+      if (!compute_objsize (ref, ostype, pref, visited, rvals))
+       return false;
+
+      offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2)));
+      pref->add_offset (off / BITS_PER_UNIT);
+      return true;
+    }
+
   if (code == COMPONENT_REF)
     {
       tree ref = TREE_OPERAND (ptr, 0);
@@ -4455,27 +4709,29 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 
       if (ostype == 0)
        {
-         /* For raw memory functions like memcpy bail if the size
-            of the enclosing object cannot be determined.  */
-         if (!compute_objsize (ref, ostype, pref, visited, rvals)
-             || !pref->ref)
+         /* In OSTYPE zero (for raw memory functions like memcpy), use
+            the maximum size instead if the identity of the enclosing
+            object cannot be determined.  */
+         if (!compute_objsize (ref, ostype, pref, visited, rvals))
            return false;
 
          /* Otherwise, use the size of the enclosing object and add
             the offset of the member to the offset computed so far.  */
          tree offset = byte_position (field);
-         if (TREE_CODE (offset) != INTEGER_CST)
-           return false;
-         offset_int off = wi::to_offset (offset);
-         pref->offrng[0] += off;
-         pref->offrng[1] += off;
+         if (TREE_CODE (offset) == INTEGER_CST)
+           pref->add_offset (wi::to_offset (offset));
+         else
+           pref->add_max_offset ();
          return true;
        }
 
-      /* Bail if the reference is to the pointer itself (as opposed
-        to what it points to).  */
       if (!addr && POINTER_TYPE_P (TREE_TYPE (field)))
-       return false;
+       {
+         /* Set maximum size if the reference is to the pointer member
+            itself (as opposed to what it points to).  */
+         pref->set_max_size_range ();
+         return true;
+       }
 
       pref->ref = field;
 
@@ -4530,9 +4786,12 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 
       offset_int orng[2];
       tree off = pref->eval (TREE_OPERAND (ptr, 1));
-      if (!get_range (off, NULL, SIGNED, orng, rvals))
-       /* Fail unless the size of the object is zero.  */
-       return pref->sizrng[0] == 0 && pref->sizrng[0] == pref->sizrng[1];
+      if (!get_offset_range (off, NULL, orng, rvals))
+       {
+         /* Set ORNG to the maximum offset representable in ptrdiff_t.  */
+         orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+         orng[0] = -orng[1] - 1;
+       }
 
       if (TREE_CODE (ptr) == ARRAY_REF)
        {
@@ -4551,7 +4810,10 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
          tree eltype = TREE_TYPE (ptr);
          tree tpsize = TYPE_SIZE_UNIT (eltype);
          if (!tpsize || TREE_CODE (tpsize) != INTEGER_CST)
-           return false;
+           {
+             pref->add_max_offset ();
+             return true;
+           }
 
          offset_int sz = wi::to_offset (tpsize);
          orng[0] *= sz;
@@ -4578,12 +4840,44 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
            }
        }
 
-      pref->offrng[0] += orng[0];
-      pref->offrng[1] += orng[1];
+      pref->add_offset (orng[0], orng[1]);
+      return true;
+    }
 
+  if (code == TARGET_MEM_REF)
+    {
+      tree ref = TREE_OPERAND (ptr, 0);
+      if (!compute_objsize (ref, ostype, pref, visited, rvals))
+       return false;
+
+      /* TODO: Handle remaining operands.  Until then, add maximum offset.  */
+      pref->ref = ptr;
+      pref->add_max_offset ();
+      return true;
+    }
+
+  if (code == INTEGER_CST)
+    {
+      /* Pointer constants other than null are most likely the result
+        of erroneous null pointer addition/subtraction.  Set size to
+        zero.  For null pointers, set size to the maximum for now
+        since those may be the result of jump threading.  */
+      if (integer_zerop (ptr))
+       pref->set_max_size_range ();
+      else
+       pref->sizrng[0] = pref->sizrng[1] = 0;
+      pref->ref = ptr;
+
+      return true;
+    }
+
+  if (code == STRING_CST)
+    {
+      pref->sizrng[0] = pref->sizrng[1] = TREE_STRING_LENGTH (ptr);
       return true;
     }
-  else if (code == POINTER_PLUS_EXPR)
+
+  if (code == POINTER_PLUS_EXPR)
     {
       tree ref = TREE_OPERAND (ptr, 0);
       if (!compute_objsize (ref, ostype, pref, visited, rvals))
@@ -4591,16 +4885,14 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 
       offset_int orng[2];
       tree off = pref->eval (TREE_OPERAND (ptr, 1));
-      if (!get_range (off, NULL, SIGNED, orng, rvals))
-       /* Fail unless the size of the object is zero.  */
-       return pref->sizrng[0] == 0 && pref->sizrng[0] == pref->sizrng[1];
-
-      pref->offrng[0] += orng[0];
-      pref->offrng[1] += orng[1];
-
+      if (get_offset_range (off, NULL, orng, rvals))
+       pref->add_offset (orng[0], orng[1]);
+      else
+       pref->add_max_offset ();
       return true;
     }
-  else if (code == VIEW_CONVERT_EXPR)
+
+  if (code == VIEW_CONVERT_EXPR)
     {
       ptr = TREE_OPERAND (ptr, 0);
       return compute_objsize (ptr, ostype, pref, visited, rvals);
@@ -4612,17 +4904,53 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
       if (is_gimple_call (stmt))
        {
          /* If STMT is a call to an allocation function get the size
-            from its argument(s).  If successful, also set *PDECL to
-            PTR for the caller to include in diagnostics.  */
+            from its argument(s).  If successful, also set *PREF->REF
+            to PTR for the caller to include in diagnostics.  */
          wide_int wr[2];
          if (gimple_call_alloc_size (stmt, wr, rvals))
            {
              pref->ref = ptr;
              pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
              pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
-             return true;
+             /* Constrain both bounds to a valid size.  */
+             offset_int maxsize = wi::to_offset (max_object_size ());
+             if (pref->sizrng[0] > maxsize)
+               pref->sizrng[0] = maxsize;
+             if (pref->sizrng[1] > maxsize)
+               pref->sizrng[1] = maxsize;
            }
-         return false;
+         else
+           {
+             /* For functions known to return one of their pointer arguments
+                try to determine what the returned pointer points to, and on
+                success add OFFRNG which was set to the offset added by
+                the function (e.g., memchr) to the overall offset.  */
+             offset_int offrng[2];
+             if (tree ret = gimple_call_return_array (stmt, offrng, rvals))
+               {
+                 if (!compute_objsize (ret, ostype, pref, visited, rvals))
+                   return false;
+
+                 /* Cap OFFRNG[1] to at most the remaining size of
+                    the object.  */
+                 offset_int remrng[2];
+                 remrng[1] = pref->size_remaining (remrng);
+                 if (remrng[1] < offrng[1])
+                   offrng[1] = remrng[1];
+                 pref->add_offset (offrng[0], offrng[1]);
+               }
+             else
+               {
+                 /* For other calls that might return arbitrary pointers
+                    including into the middle of objects set the size
+                    range to maximum, clear PREF->BASE0, and also set
+                    PREF->REF to include in diagnostics.  */
+                 pref->set_max_size_range ();
+                 pref->base0 = false;
+                 pref->ref = ptr;
+               }
+           }
+         return true;
        }
 
       if (gimple_nop_p (stmt))
@@ -4631,62 +4959,74 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
             of the array from the current function declaratation
             (e.g., attribute access or related).  */
          wide_int wr[2];
-         tree ref = gimple_parm_array_size (ptr, wr, rvals);
-         if (!ref)
-           return NULL_TREE;
-         pref->ref = ref;
-         pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
-         pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
+         if (tree ref = gimple_parm_array_size (ptr, wr, rvals))
+           {
+             pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
+             pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
+             pref->ref = ref;
+             return true;
+           }
+
+         pref->set_max_size_range ();
+         pref->base0 = false;
+         pref->ref = ptr;
+         if (tree var = SSA_NAME_VAR (ptr))
+           if (TREE_CODE (var) == PARM_DECL)
+             pref->ref = var;
+
          return true;
        }
 
       /* TODO: Handle PHI.  */
 
       if (!is_gimple_assign (stmt))
-       return false;
+       {
+         /* Clear BASE0 since the assigned pointer might point into
+            the middle of the object, set the maximum size range and,
+            if the SSA_NAME refers to a function argumnent, set
+            PREF->REF to it.  */
+         pref->base0 = false;
+         pref->set_max_size_range ();
+         if (tree var = SSA_NAME_VAR (ptr))
+           if (TREE_CODE (var) == PARM_DECL)
+             pref->ref = var;
+         return true;
+       }
 
       ptr = gimple_assign_rhs1 (stmt);
 
       tree_code code = gimple_assign_rhs_code (stmt);
-      if (TREE_CODE (TREE_TYPE (ptr)) != POINTER_TYPE)
-       /* Avoid conversions from non-pointers.  */
-       return false;
 
-      if (code == POINTER_PLUS_EXPR)
+      if (code == POINTER_PLUS_EXPR
+         && TREE_CODE (TREE_TYPE (ptr)) == POINTER_TYPE)
        {
-         /* If the the offset in the expression can be determined use
-            it to adjust the overall offset.  Otherwise, set the overall
-            offset to the maximum.  */
+         /* Compute the size of the object first. */
+         if (!compute_objsize (ptr, ostype, pref, visited, rvals))
+           return false;
+
          offset_int orng[2];
          tree off = gimple_assign_rhs2 (stmt);
-         if (!get_range (off, stmt, SIGNED, orng, rvals))
-           {
-             orng[0] = wi::to_offset (TYPE_MIN_VALUE (ptrdiff_type_node));
-             orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
-           }
-
-         pref->offrng[0] += orng[0];
-         pref->offrng[1] += orng[1];
+         if (get_offset_range (off, stmt, orng, rvals))
+           pref->add_offset (orng[0], orng[1]);
+         else
+           pref->add_max_offset ();
+         return true;
        }
-      else if (code != ADDR_EXPR)
-       return false;
 
-      return compute_objsize (ptr, ostype, pref, visited, rvals);
-    }
-
-  tree type = TREE_TYPE (ptr);
-  type = TYPE_MAIN_VARIANT (type);
-  if (TREE_CODE (ptr) == ADDR_EXPR)
-    ptr = TREE_OPERAND (ptr, 0);
+      if (code == ADDR_EXPR)
+       return compute_objsize (ptr, ostype, pref, visited, rvals);
 
-  if (TREE_CODE (type) == ARRAY_TYPE
-      && !array_at_struct_end_p (ptr))
-    {
-      if (tree size = TYPE_SIZE_UNIT (type))
-       return get_range (size, NULL, UNSIGNED, pref->sizrng, rvals);
+      /* This could be an assignment from a nonlocal pointer.  Save PTR
+        to mention in diagnostics but otherwise treat it as a pointer
+        to an unknown object.  */
+      pref->ref = ptr;
     }
 
-  return false;
+  /* Assume all other expressions point into an unknown object
+     of the maximum valid size.  */
+  pref->base0 = false;
+  pref->set_max_size_range ();
+  return true;
 }
 
 /* A "public" wrapper around the above.  Clients should use this overload
@@ -4707,27 +5047,10 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
   if (!success)
     return NULL_TREE;
 
-  if (pref->offrng[1] < pref->offrng[0])
-    {
-      if (pref->offrng[1] < 0
-         && pref->sizrng[1] <= pref->offrng[0])
-       return size_zero_node;
-
-      return wide_int_to_tree (sizetype, pref->sizrng[1]);
-    }
-
-  if (pref->offrng[0] < 0)
-    {
-      if (pref->offrng[1] < 0)
-       return size_zero_node;
-
-      pref->offrng[0] = 0;
-    }
-
-  if (pref->sizrng[1] <= pref->offrng[0])
-    return size_zero_node;
-
-  return wide_int_to_tree (sizetype, pref->sizrng[1] - pref->offrng[0]);
+  offset_int maxsize = pref->size_remaining ();
+  if (pref->base0 && pref->offrng[0] < 0 && pref->offrng[1] >= 0)
+    pref->offrng[0] = 0;
+  return wide_int_to_tree (sizetype, maxsize);
 }
 
 /* Transitional wrapper around the above.  The function should be removed
@@ -4735,13 +5058,13 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
 
 tree
 compute_objsize (tree ptr, int ostype, tree *pdecl /* = NULL */,
-                tree *poff /* = NULL */, class range_query *rvals /* = NULL */)
+                tree *poff /* = NULL */, range_query *rvals /* = NULL */)
 {
   /* Set the initial offsets to zero and size to negative to indicate
      none has been computed yet.  */
   access_ref ref;
   tree size = compute_objsize (ptr, ostype, &ref, rvals);
-  if (!size)
+  if (!size || !ref.base0)
     return NULL_TREE;
 
   if (pdecl)
@@ -6020,13 +6343,21 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
          tree size2 = compute_objsize (arg2, 1, &ref2);
          tree func = get_callee_fndecl (exp);
 
-         if (size1 && size2)
+         if (size1 && size2 && bndrng[0] && !integer_zerop (bndrng[0]))
            {
-             tree maxsize = tree_int_cst_le (size1, size2) ? size2 : size1;
-
-             if (tree_int_cst_lt (maxsize, bndrng[0]))
+             offset_int rem1 = ref1.size_remaining ();
+             offset_int rem2 = ref2.size_remaining ();
+             if (rem1 == 0 || rem2 == 0)
                maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp, func,
-                                     bndrng, maxsize);
+                                     bndrng, integer_zero_node);
+             else
+               {
+                 offset_int maxrem = wi::max (rem1, rem2, UNSIGNED);
+                 if (maxrem < wi::to_offset (bndrng[0]))
+                   maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp,
+                                         func, bndrng,
+                                         wide_int_to_tree (sizetype, maxrem));
+               }
            }
          else if (bndrng[0]
                   && !integer_zerop (bndrng[0])
index f226c63e19861ebe2afa2066f26f8bd4c029c0e1..c09f36da02b76bea2ce6df700c05642fe2603a03 100644 (file)
@@ -180,15 +180,45 @@ struct access_ref
     return offrng[0] == 0 && offrng[1] == 0;
   }
 
-  /* Return true if OFFRNG is bounded to a subrange of possible offset
-     values.  */
+  /* Return true if OFFRNG is bounded to a subrange of offset values
+     valid for the largest possible object.  */
   bool offset_bounded () const;
 
+  /* Return the maximum amount of space remaining and if non-null, set
+     argument to the minimum.  */
+  offset_int size_remaining (offset_int * = NULL) const;
+
+  /* Set the size range to the maximum.  */
+  void set_max_size_range ()
+  {
+    sizrng[0] = 0;
+    sizrng[1] = wi::to_offset (max_object_size ());
+  }
+
+  /* Add OFF to the offset range.  */
+  void add_offset (const offset_int &off)
+  {
+    add_offset (off, off);
+  }
+
+  /* Add the range [MIN, MAX] to the offset range.  */
+  void add_offset (const offset_int &, const offset_int &);
+
+  /* Add the maximum representable offset to the offset range.  */
+  void add_max_offset ()
+  {
+    offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+    add_offset (-maxoff - 1, maxoff);
+  }
+
   /* Used to fold integer expressions when called from front ends.  */
   tree (*eval)(tree);
   /* Set if trailing one-element arrays should be treated as flexible
      array members.  */
   bool trail1special;
+  /* Set if valid offsets must start at zero (for declared and allocated
+     objects but not for others referenced by pointers).  */
+  bool base0;
 };
 
 /* Describes a pair of references used in an access by built-in
@@ -216,11 +246,10 @@ class range_query;
 extern tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
                                    range_query * = NULL);
 extern tree gimple_parm_array_size (tree, wide_int[2], range_query * = NULL);
+extern tree compute_objsize (tree, int, access_ref *, range_query * = NULL);
 extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
                             range_query * = NULL);
-extern tree compute_objsize (tree, int, access_ref *, range_query * = NULL);
-
-extern bool check_access (tree, tree, tree, tree, tree, access_mode,
-                         const access_data * = NULL);
+extern bool check_access (tree, tree, tree, tree, tree,
+                         access_mode, const access_data * = NULL);
 
 #endif /* GCC_BUILTINS_H */
index 93da3d6256e67a8b4ae427aee85ba59af65c40da..d3120b23f6066b9cc50cfa382e9e24e4aa012deb 100644 (file)
@@ -1235,14 +1235,16 @@ alloc_max_size (void)
    after adjusting it if necessary to make EXP a represents a valid size
    of object, or a valid size argument to an allocation function declared
    with attribute alloc_size (whose argument may be signed), or to a string
-   manipulation function like memset.  When ALLOW_ZERO is true, allow
-   returning a range of [0, 0] for a size in an anti-range [1, N] where
-   N > PTRDIFF_MAX.  A zero range is a (nearly) invalid argument to
-   allocation functions like malloc but it is a valid argument to
-   functions like memset.  */
+   manipulation function like memset.
+   When ALLOW_ZERO is set in FLAGS, allow returning a range of [0, 0] for
+   a size in an anti-range [1, N] where N > PTRDIFF_MAX.  A zero range is
+   a (nearly) invalid argument to allocation functions like malloc but it
+   is a valid argument to functions like memset.
+   When USE_LARGEST is set in FLAGS set RANGE to the largest valid subrange
+   in a multi-range, otherwise to the smallest valid subrange.  */
 
 bool
-get_size_range (tree exp, tree range[2], bool allow_zero /* = false */)
+get_size_range (tree exp, tree range[2], int flags /* = 0 */)
 {
   if (!exp)
     return false;
@@ -1314,25 +1316,50 @@ get_size_range (tree exp, tree range[2], bool allow_zero /* = false */)
              min = wi::zero (expprec);
            }
        }
-      else if (wi::eq_p (0, min - 1))
+      else
        {
-         /* EXP is unsigned and not in the range [1, MAX].  That means
-            it's either zero or greater than MAX.  Even though 0 would
-            normally be detected by -Walloc-zero, unless ALLOW_ZERO
-            is true, set the range to [MAX, TYPE_MAX] so that when MAX
-            is greater than the limit the whole range is diagnosed.  */
-         if (allow_zero)
-           min = max = wi::zero (expprec);
-         else
+         wide_int maxsize = wi::to_wide (max_object_size ());
+         min = wide_int::from (min, maxsize.get_precision (), UNSIGNED);
+         max = wide_int::from (max, maxsize.get_precision (), UNSIGNED);
+         if (wi::eq_p (0, min - 1))
            {
+             /* EXP is unsigned and not in the range [1, MAX].  That means
+                it's either zero or greater than MAX.  Even though 0 would
+                normally be detected by -Walloc-zero, unless ALLOW_ZERO
+                is set, set the range to [MAX, TYPE_MAX] so that when MAX
+                is greater than the limit the whole range is diagnosed.  */
+             wide_int maxsize = wi::to_wide (max_object_size ());
+             if (flags & SR_ALLOW_ZERO)
+               {
+                 if (wi::leu_p (maxsize, max + 1)
+                     || !(flags & SR_USE_LARGEST))
+                   min = max = wi::zero (expprec);
+                 else
+                   {
+                     min = max + 1;
+                     max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+                   }
+               }
+             else
+               {
+                 min = max + 1;
+                 max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+               }
+           }
+         else if ((flags & SR_USE_LARGEST)
+                  && wi::ltu_p (max + 1, maxsize))
+           {
+             /* When USE_LARGEST is set and the larger of the two subranges
+                is a valid size, use it...  */
              min = max + 1;
-             max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+             max = maxsize;
+           }
+         else
+           {
+             /* ...otherwise use the smaller subrange.  */
+             max = min - 1;
+             min = wi::zero (expprec);
            }
-       }
-      else
-       {
-         max = min - 1;
-         min = wi::zero (expprec);
        }
     }
 
index dfb951ca45b94e2545463fefead41db673e4596c..644ec45d92c4abc2e5fb87bac9f71a40b9fc4f28 100644 (file)
@@ -133,7 +133,15 @@ extern bool reference_callee_copied (CUMULATIVE_ARGS *,
 extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]);
 extern tree get_attr_nonstring_decl (tree, tree * = NULL);
 extern bool maybe_warn_nonstring_arg (tree, tree);
-extern bool get_size_range (tree, tree[2], bool = false);
+enum size_range_flags
+  {
+   /* Set to consider zero a valid range.  */
+   SR_ALLOW_ZERO = 1,
+   /* Set to use the largest subrange of a set of ranges as opposed
+      to the smallest.  */
+   SR_USE_LARGEST = 2
+  };
+extern bool get_size_range (tree, tree[2], int = 0);
 extern rtx rtx_for_static_chain (const_tree, bool);
 extern bool cxx17_empty_base_field_p (const_tree);
 
index 512fc138528e97107844eccf39e885c15d45ce1c..e2734c81456940497964bd55af2246653bd33e00 100644 (file)
@@ -264,7 +264,7 @@ builtin_memref::builtin_memref (tree expr, tree size)
       tree range[2];
       /* Determine the size range, allowing for the result to be [0, 0]
         for SIZE in the anti-range ~[0, N] where N >= PTRDIFF_MAX.  */
-      get_size_range (size, range, true);
+      get_size_range (size, range, SR_ALLOW_ZERO);
       sizrange[0] = wi::to_offset (range[0]);
       sizrange[1] = wi::to_offset (range[1]);
       /* get_size_range returns SIZE_MAX for the maximum size.
index 3b019c8a80ebfd5b3a1ed654c30a185bb6317de0..9eb02bdbfcb172ee2ccd1477096f9d6fe4588fa2 100644 (file)
@@ -320,14 +320,17 @@ void test_memcpy_anti_range (char *d, const char *s)
 
   T (d, d + SAR (0, 3), UR (DIFF_MAX - 2, DIFF_MAX));               /* { dg-warning "accessing \[0-9\]+ or more bytes at offsets 0 and \\\[-?\[0-9\]+, -?\[0-9\]+] overlaps \[0-9\]+ bytes at offset 2" "memcpy" } */
 
-  /* Verify that a size in an anti-range ~[0, N] where N >= PTRDIFF_MAX
-     doesn't trigger a warning.  */
+  /* Verify that a size in an anti-range ~[1, N] where N >= PTRDIFF_MAX - 2
+     doesn't trigger a warning.
+     With ~[1, PTRDIFF_MAX - 1] the difference between the just-past-the-end
+     pointer to A and A for char A[PTRDIFF_MAX] wouldn't be representable in
+     ptrdiff_t.  Since such a large object cannot exist, so the size of
+     the region must be zero.  */
+  T (d, s, UAR (1, DIFF_MAX / 2 - 1));
   T (d, s, UAR (1, DIFF_MAX - 1));
   T (d, s, UAR (1, DIFF_MAX));
   T (d, s, UAR (1, SIZE_MAX - 1));
-
-  /* This causes the last dg-warning test to fail for some reason.
-     T (d, s, UAR (1, SIZE_MAX)); */
+  T (d, s, UAR (1, SIZE_MAX));
 }
 
 /* Verify calls to memcpy() where the combination of offsets in some
diff --git a/gcc/testsuite/g++.dg/warn/Wplacement-new-size-8.C b/gcc/testsuite/g++.dg/warn/Wplacement-new-size-8.C
new file mode 100644 (file)
index 0000000..a0d1362
--- /dev/null
@@ -0,0 +1,147 @@
+/* Verify informational notes following the warning.
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+#define DISS_MAX  __PTRDIFF_MAX__
+#define SIZE_MAX  __SIZE_MAX__
+
+typedef __SIZE_TYPE__  size_t;
+
+template <int N> struct S { char a[N]; };
+
+void* operator new (size_t, void *p) { return p; }
+void* operator new[] (size_t, void *p) { return p; }
+
+
+void test_cst_off ()
+{
+  {
+    char ca0[0];                // { dg-message "'ca0' declared here" "note" }
+    new (ca0 + 0) S<1>;         // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[0]' and size '0'" }
+  }
+  {
+    char ca1[1];
+    new (ca1 + 0) S<1>;
+  }
+  {
+    char ca1[1];                // { dg-message "'ca1' declared here" "note" }
+    new (ca1 + 0) S<2>;         // { dg-warning "constructing an object of type 'S<2>' and size '2' in a region of type 'char \\\[1]' and size '1'" }
+  }
+  {
+    char ca1[1];                // { dg-message "at offset 1 from 'ca1' declared here" "note" }
+    new (ca1 + 1) S<1>;         // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[1]' and size '0'" }
+  }
+  {
+    char ca1[1];                // { dg-message "at offset 2 from 'ca1' declared here" "note" }
+    new (ca1 + 2) S<1>;         // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[1]' and size '0'" }
+  }
+  {
+    char ca1[1];                // { dg-message "at offset -1 from 'ca1' declared here" "note" }
+    new (ca1 - 1) S<1>;         // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[1]' and size '0'" }
+  }
+  {
+    /* Offsets are treated as signed so SIZE_MAX is indistinguishable
+       from -1.  */
+    char ca1[1];                // { dg-message "at offset \\d+ from 'ca1' declared here" "note" { xfail *-*-* } }
+                                // { dg-message "at offset -1 from 'ca1' declared here" "note" { target *-*-* } .-1 }
+    new (ca1 + SIZE_MAX) S<1>;  // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[1]' and size '0'" }
+  }
+}
+
+
+/* Verify that the range of the offset included in the note corresponds
+   to the range of its type (plus the optional constant).  */
+
+void test_var_off_uchar (unsigned char i)
+{
+  {
+    // Verify that the nore doesn't mention an offset.
+    char ca0[0];                // { dg-message ": 'ca0' declared here" "note" }
+    new (ca0 + i) S<1>;         // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[0]' and size '0'" }
+  }
+  {
+    char ca1[1];
+    new (ca1 + i) S<1>;
+  }
+  {
+    // Verify that the nore doesn't mention an offset.
+    char ca1[1];                // { dg-message ": 'ca1' declared here" "note" }
+    new (ca1 + i) S<2>;         // { dg-warning "constructing an object of type 'S<2>' and size '2' in a region of type 'char \\\[1]' and size at most '1'" }
+  }
+  {
+    char ca2[2];
+    new (ca2 + i) S<2>;
+    new (ca2 + 1 - i) S<2>;
+    new (ca2 - i + 1) S<2>;
+    new (ca2 - 2 + i) S<2>;
+    new (ca2 - i + 2) S<2>;
+    new (ca2 - i + i) S<2>;
+    new (ca2 + i + i) S<2>;
+  }
+  {
+    char ca2[2];                // { dg-message "at offset \\\[1, 2] from 'ca2' declared here" "note" }
+    new (ca2 + i + 1) S<2>;     // { dg-warning "constructing an object of type 'S<2>' and size '2' in a region of type 'char \\\[2]' and size at most '1'" }
+  }
+
+  {
+    char a[65281];
+    new (a + i + 65280) S<1>;
+  }
+  {
+    char a[65281];              // { dg-message "at offset 65281 from 'a' declared here" "note" }
+    new (a + i + 65281) S<1>;   // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[65281]' and size '0'" }
+  }
+  {
+    char a[65281];              // { dg-message "at offset \\\[65154, 65281] from 'a' declared here" "note" }
+    new (a + i + 65154) S<128>; // { dg-warning "constructing an object of type 'S<128>' and size '128' in a region of type 'char \\\[65281]' and size at most '127'" }
+  }
+}
+
+
+/* Same as above but also verify that the signedness of the offset is
+   considered in the issuing the warning.  */
+
+void test_var_off_schar (signed char i)
+{
+  {
+    // Verify that the nore doesn't mention an offset.
+    char ca0[0];                // { dg-message ": 'ca0' declared here" "note" }
+    new (ca0 + i) S<1>;         // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[0]' and size '0'" }
+  }
+  {
+    char ca1[1];
+    new (ca1 + i) S<1>;
+    new (ca1 - i) S<1>;
+    new (ca1 + i + 1) S<1>;
+    new (ca1 - i + 1) S<1>;
+    new (ca1 + i + i) S<1>;
+    new (ca1 - i - i) S<1>;
+  }
+  {
+    // Verify that the nore doesn't mention an offset.
+    char ca1[1];                // { dg-message ": 'ca1' declared here" "note" }
+    new (ca1 + i) S<2>;         // { dg-warning "constructing an object of type 'S<2>' and size '2' in a region of type 'char \\\[1]' and size at most '1'" }
+  }
+  {
+    char ca2[2];
+    new (ca2 + i) S<2>;
+    new (ca2 + 1 - i) S<2>;
+    new (ca2 - i + 1) S<2>;
+    new (ca2 - 2 + i) S<2>;
+    new (ca2 - i + 2) S<2>;
+    new (ca2 - i + i) S<2>;
+    new (ca2 + i + i) S<2>;
+  }
+  {
+    char ca2[2];
+    new (ca2 + i + 1) S<2>;
+  }
+
+  {
+    char a[65281];              // { dg-message "at offset \\\[65153, 65408] from 'a'" }
+    new (a + i + 65280) S<1>;
+    new (a + i + 65281) S<1>;
+    new (a + i + 65281) S<128>;
+    new (a + i + 65281) S<129>; // { dg-warning "constructing an object of type 'S<129>' and size '129' in a region of type 'char \\\[65281]' and size at most '128'" }
+  }
+}
index fd43f3afb59fcb18956d40dc4ebd0c2aa45a3892..a1b103918cff509d241217875d74e3a7a9e3678e 100644 (file)
@@ -112,7 +112,7 @@ void s2_warn_cstoff_cstidx (struct S2 *p)
 void s2_warn_varoff_cstdix (struct S2 *p, int i)
 {
   char *q = p->a + i;
-  q[2] = __LINE__;            // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* }  }
+  q[2] = __LINE__;            // { dg-warning "\\\[-Wstringop-overflow" }
 }
 
 void s2_warn_cstoff_varidx (struct S2 *p, int i)
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-41.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-41.c
new file mode 100644 (file)
index 0000000..9b2d2cb
--- /dev/null
@@ -0,0 +1,120 @@
+/* Verify that writes at excessive offsets into declared or allocated
+   objects of unknown size are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2" } */
+
+#define DIFF_MAX __PTRDIFF_MAX__
+
+typedef __SIZE_TYPE__ size_t;
+
+void* malloc (size_t);
+void* memcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+
+void sink (void*);
+
+
+void char_array_cst_off_cst_size (void)
+{
+  extern char caxcc[];                  // { dg-message "at offset \\d+ into destination object 'caxcc'" }
+
+  char *p = caxcc;
+  size_t idx = DIFF_MAX - 3;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 2" }
+  sink (p);
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 1" "pr?????" { xfail ilp32 } }
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 0" }
+  sink (p);
+}
+
+
+void char_array_var_off_cst_size (size_t idx)
+{
+  /* The offset is a range with a very large lower bound and an upper
+     bound of DIFF_MAX.  There's not point in also mentioning the latter
+     (it wouldn't make the note any more meaningful) so verify it only
+     mentions the lower bound.  */
+  extern char caxvc[];                  // { dg-message "at offset \\d+ into destination object 'caxvc'" "note" }
+
+  char *p = caxvc;
+
+  if (idx < DIFF_MAX - 3)
+    idx = DIFF_MAX - 3;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  memset (p + idx, 0, 5);               // { dg-warning "writing 5 bytes into a region of size 3" }
+  sink (p);
+}
+
+
+void char_array_var_off_var_size (size_t idx, size_t n)
+{
+  extern char caxvv[];                  // { dg-message "at offset \\d+ into destination object 'caxvv'" "note" }
+
+  char *p = caxvv;
+
+  if (idx < DIFF_MAX - 3)
+    idx = DIFF_MAX - 3;
+
+  if (n < 3 || 7 < n)
+    n = 3;
+
+  memset (p + idx, 0, n);
+  sink (p);
+
+  ++n;
+  memset (p + idx, 0, n);               // { dg-warning "writing between 4 and 8 bytes into a region of size 3" }
+  sink (p);
+}
+
+
+void alloc_array_var_off_cst_size (size_t n, size_t idx)
+{
+  char *p = malloc (n);                 // { dg-message "at offset \\d+ into destination object" "note" }
+
+  if (idx < DIFF_MAX - 3)
+    idx = DIFF_MAX - 3;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  memset (p + idx, 0, 5);               // { dg-warning "writing 5 bytes into a region of size 3" }
+  sink (p);
+}
+
+
+void int_array_cst_off_cst_size (void)
+{
+  extern int iaxc[];                    // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'iaxc'" }
+
+  int *p = iaxc;
+  size_t idx = DIFF_MAX / sizeof *iaxc;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  memset (p + idx, 0, 5);               // { dg-warning "writing 5 bytes into a region of size 3" }
+  sink (p);
+}
+
+
+void* nowarn_anti_range_1 (char *p, char *q)
+{
+  size_t n = q - p;
+  if (!n) return 0;
+
+  char *d = __builtin_malloc (n + 1);
+  memcpy (d, p, n + 1);                 // { dg-bogus "-Wstringop-overflow" }
+  return d;
+}
index 3ac5a88e4b0828723e034ae8d348553a2101df46..14ab925afdce9263582f3a0aecc7c29f3b29df54 100644 (file)
@@ -159,9 +159,10 @@ void warn_memset_reversed_range (void)
 
   char *p = &a11[11];
 
-  /* The below is represented as a true anti-range as opposed to a range
-     with reversed bounds and the former aren't handled.  */
-  T1 (p, SAR (INT_MIN, -11), n11);      // { dg-warning "writing 11 or more bytes into a region of size 0" "pr?????" { xfail *-*-* } }
+  /* Since the offset is excessive, either starting before &a11[0]
+     ot just past &a[11], the region size in the warning should
+     probably be zero, but accept other sizes too.  */
+  T1 (p, SAR (INT_MIN, -11), n11);      // { dg-warning "writing 11 or more bytes into a region of size \\d+" }
 
   /* The following are represented as ordinary ranges with reversed bounds
      and those are handled. */
@@ -171,7 +172,7 @@ void warn_memset_reversed_range (void)
   T1 (p, SAR (INT_MIN,   1), n11);      // { dg-warning "writing 11 or more bytes into a region of size 0" "pr?????" { xfail ilp32 } }
   T1 (p, SAR (INT_MIN,   0), n11);      // { dg-warning "writing 11 or more bytes into a region of size 0" }
   /* Also represented as a true anti-range.  */
-  T1 (p, SAR (    -12, -11), n11);      // { dg-warning "writing 11 or more bytes into a region of size 0" "pr?????" { xfail *-*-* } }
+  T1 (p, SAR (    -12, -11), n11);      // { dg-warning "writing 11 or more bytes into a region of size \\d+" }
   T1 (p, SAR (    -12,  -1), n11);      // { dg-warning "writing 11 or more bytes into a region of size 0" }
   T1 (p, SAR (    -11,   0), n11);      // { dg-warning "writing 11 or more bytes into a region of size 0" }
   T1 (p, SAR (    -11,  11), n11);      // { dg-warning "writing 11 or more bytes into a region of size 0" }
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-44.s b/gcc/testsuite/gcc.dg/Wstringop-overflow-44.s
new file mode 100644 (file)
index 0000000..0fc73a9
--- /dev/null
@@ -0,0 +1,271 @@
+       .file   "Wstringop-overflow-44.c"
+       .text
+       .p2align 4
+       .globl  f0
+       .type   f0, @function
+f0:
+.LFB0:
+       .cfi_startproc
+       ret
+       .cfi_endproc
+.LFE0:
+       .size   f0, .-f0
+       .p2align 4
+       .globl  f1
+       .type   f1, @function
+f1:
+.LFB1:
+       .cfi_startproc
+       ret
+       .cfi_endproc
+.LFE1:
+       .size   f1, .-f1
+       .p2align 4
+       .globl  f2
+       .type   f2, @function
+f2:
+.LFB2:
+       .cfi_startproc
+       movl    n(%rip), %eax
+       testl   %eax, %eax
+       jle     .L12
+.L4:
+       ret
+       .p2align 4,,10
+       .p2align 3
+.L12:
+       movslq  %eax, %rdx
+       movq    d(%rip), %rcx
+       testq   %rdx, %rdx
+       je      .L4
+       xorl    %eax, %eax
+.L6:
+       movb    $0, (%rcx,%rax)
+       addq    $1, %rax
+       cmpq    %rdx, %rax
+       jb      .L6
+       ret
+       .cfi_endproc
+.LFE2:
+       .size   f2, .-f2
+       .p2align 4
+       .globl  f3
+       .type   f3, @function
+f3:
+.LFB3:
+       .cfi_startproc
+       movslq  n(%rip), %rdx
+       testl   %edx, %edx
+       jle     .L15
+       ret
+       .p2align 4,,10
+       .p2align 3
+.L15:
+       movq    %rdi, %rsi
+       movq    d(%rip), %rdi
+       jmp     strncpy
+       .cfi_endproc
+.LFE3:
+       .size   f3, .-f3
+       .p2align 4
+       .globl  f4
+       .type   f4, @function
+f4:
+.LFB4:
+       .cfi_startproc
+       movl    n(%rip), %eax
+       testl   %eax, %eax
+       jle     .L18
+       ret
+       .p2align 4,,10
+       .p2align 3
+.L18:
+       movq    d(%rip), %rax
+       movq    %rdi, %rsi
+       movb    $0, (%rax)
+       movslq  n(%rip), %rdx
+       movq    d(%rip), %rdi
+       jmp     strncat
+       .cfi_endproc
+.LFE4:
+       .size   f4, .-f4
+       .p2align 4
+       .globl  g0
+       .type   g0, @function
+g0:
+.LFB5:
+       .cfi_startproc
+       movl    n(%rip), %eax
+       testl   %eax, %eax
+       jle     .L25
+       ret
+       .p2align 4,,10
+       .p2align 3
+.L25:
+       subq    $24, %rsp
+       .cfi_def_cfa_offset 32
+       leaq    15(%rsp), %rdi
+       call    sink
+       addq    $24, %rsp
+       .cfi_def_cfa_offset 8
+       ret
+       .cfi_endproc
+.LFE5:
+       .size   g0, .-g0
+       .p2align 4
+       .globl  g1
+       .type   g1, @function
+g1:
+.LFB6:
+       .cfi_startproc
+       movl    n(%rip), %eax
+       testl   %eax, %eax
+       jle     .L32
+       ret
+       .p2align 4,,10
+       .p2align 3
+.L32:
+       subq    $24, %rsp
+       .cfi_def_cfa_offset 32
+       leaq    15(%rsp), %rdi
+       call    sink
+       addq    $24, %rsp
+       .cfi_def_cfa_offset 8
+       ret
+       .cfi_endproc
+.LFE6:
+       .size   g1, .-g1
+       .p2align 4
+       .globl  g2
+       .type   g2, @function
+g2:
+.LFB7:
+       .cfi_startproc
+       movl    n(%rip), %eax
+       testl   %eax, %eax
+       jle     .L45
+       ret
+       .p2align 4,,10
+       .p2align 3
+.L45:
+       movslq  %eax, %rdx
+       subq    $24, %rsp
+       .cfi_def_cfa_offset 32
+       testq   %rdx, %rdx
+       je      .L36
+       xorl    %eax, %eax
+.L35:
+       movb    $0, 15(%rsp,%rax)
+       addq    $1, %rax
+       cmpq    %rdx, %rax
+       jb      .L35
+.L36:
+       leaq    15(%rsp), %rdi
+       call    sink
+       addq    $24, %rsp
+       .cfi_def_cfa_offset 8
+       ret
+       .cfi_endproc
+.LFE7:
+       .size   g2, .-g2
+       .p2align 4
+       .globl  g3
+       .type   g3, @function
+g3:
+.LFB8:
+       .cfi_startproc
+       movslq  n(%rip), %rdx
+       testl   %edx, %edx
+       jle     .L52
+       ret
+       .p2align 4,,10
+       .p2align 3
+.L52:
+       subq    $24, %rsp
+       .cfi_def_cfa_offset 32
+       movq    %rdi, %rsi
+       leaq    15(%rsp), %rdi
+       call    strncpy
+       leaq    15(%rsp), %rdi
+       call    sink
+       addq    $24, %rsp
+       .cfi_def_cfa_offset 8
+       ret
+       .cfi_endproc
+.LFE8:
+       .size   g3, .-g3
+       .p2align 4
+       .globl  g4
+       .type   g4, @function
+g4:
+.LFB9:
+       .cfi_startproc
+       movslq  n(%rip), %rdx
+       testl   %edx, %edx
+       jle     .L59
+       ret
+       .p2align 4,,10
+       .p2align 3
+.L59:
+       subq    $24, %rsp
+       .cfi_def_cfa_offset 32
+       movq    %rdi, %rsi
+       leaq    15(%rsp), %rdi
+       movb    $0, 15(%rsp)
+       call    strncat
+       leaq    15(%rsp), %rdi
+       call    sink
+       addq    $24, %rsp
+       .cfi_def_cfa_offset 8
+       ret
+       .cfi_endproc
+.LFE9:
+       .size   g4, .-g4
+       .p2align 4
+       .globl  h0
+       .type   h0, @function
+h0:
+.LFB10:
+       .cfi_startproc
+       movl    n(%rip), %eax
+       testl   %eax, %eax
+       jle     .L66
+       ret
+       .p2align 4,,10
+       .p2align 3
+.L66:
+       subq    $8, %rsp
+       .cfi_def_cfa_offset 16
+       movl    $1, %edi
+       call    malloc
+       movq    %rax, d(%rip)
+       addq    $8, %rsp
+       .cfi_def_cfa_offset 8
+       ret
+       .cfi_endproc
+.LFE10:
+       .size   h0, .-h0
+       .p2align 4
+       .globl  h1
+       .type   h1, @function
+h1:
+.LFB16:
+       .cfi_startproc
+       movl    n(%rip), %eax
+       testl   %eax, %eax
+       jle     .L73
+       ret
+       .p2align 4,,10
+       .p2align 3
+.L73:
+       subq    $8, %rsp
+       .cfi_def_cfa_offset 16
+       movl    $1, %edi
+       call    malloc
+       movq    %rax, d(%rip)
+       addq    $8, %rsp
+       .cfi_def_cfa_offset 8
+       ret
+       .cfi_endproc
+.LFE16:
+       .size   h1, .-h1
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-45.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-45.c
new file mode 100644 (file)
index 0000000..112d79a
--- /dev/null
@@ -0,0 +1,255 @@
+/* PR middle-end/97023 - missing warning on buffer overflow in chained mempcpy
+   Verify that out of bounds writes by built-ins to objects through pointers
+   returned by other built-ins are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2" } */
+
+#include "range.h"
+
+void* malloc (size_t);
+void* memcpy (void*, const void*, size_t);
+void* memmove (void*, const void*, size_t);
+void* mempcpy (void*, const void*, size_t);
+
+void sink (void*, ...);
+
+
+void nowarn_memcpy (const void *s)
+{
+  extern char cpy_a4[4];
+  unsigned n = sizeof cpy_a4;
+
+  void *p = cpy_a4;
+  p = memcpy (p, s, n);
+  sink (p);
+  memcpy (p, s, n);
+  sink (p);
+
+  p = cpy_a4 + 1;
+  p = memcpy (p, s, n - 1);
+  sink (p);
+  memcpy (p, s, n - 1);
+  sink (p);
+
+  p = cpy_a4 + 2;
+  p = memcpy (p, s, n - 2);
+  sink (p);
+  memcpy (p, s, n - 2);
+  sink (p);
+
+  p = cpy_a4 + 3;
+  p = memcpy (p, s, n - 3);
+  sink (p);
+  memcpy (p, s, n - 3);
+  sink (p);
+
+  p = cpy_a4 + 4;
+  p = memcpy (p, s, n - 4);
+  sink (p);
+  memcpy (p, s, n - 4);
+  sink (p);
+}
+
+
+void nowarn_memcpy_chain (const void *s)
+{
+  extern char cpy_a8[8];
+
+  char *p = cpy_a8;
+
+  p = memcpy (p + 1, s, 7);
+  sink (p);
+
+  p = memcpy (p + 2 , s, 5);
+  sink (p);
+
+  p = memcpy (p + 3 , s, 2);
+  sink (p);
+
+  p = memcpy (p + 1 , s, 1);
+  sink (p);
+
+  p = memcpy (p - 7 , s, 8);
+  sink (p);
+
+  memcpy (p + 1, s, 7);
+}
+
+
+void warn_memcpy (const void *s)
+{
+  extern char cpy_a5[5];                // { dg-message "destination object 'cpy_a5'" "note" }
+
+  unsigned n = sizeof cpy_a5;
+  void *p = cpy_a5;
+
+  p = memcpy (p, s, n);
+  sink (p);
+  memcpy (p, s, n + 1);                 // { dg-warning "writing 6 bytes into a region of size 5" }
+  sink (p);
+
+  p = cpy_a5;
+  p = memcpy (p, s, n);
+  sink (p);
+  memcpy (p, s, n + 1);                 // { dg-warning "writing 6 bytes into a region of size 5" }
+  sink (p);
+
+  p = cpy_a5 + 1;
+  p = memcpy (p, s, n - 1);
+  sink (p);
+  memcpy (p, s, n);                     // { dg-warning "writing 5 bytes into a region of size 4" }
+  sink (p);
+}
+
+
+void warn_memcpy_chain (const void *s)
+{
+  extern char cpy_a8[8];                // { dg-message "destination object 'cpy_a8'" "note" }
+
+  char *p = cpy_a8;
+
+  p = memcpy (p, s, 9);                 // { dg-warning "writing 9 bytes into a region of size 8" }
+  sink (p);
+
+  p = memcpy (p + 2, s, 7);             // { dg-warning "writing 7 bytes into a region of size 6" }
+  sink (p);
+
+  p = memcpy (p + 3, s, 5);             // { dg-warning "writing 5 bytes into a region of size 3" }
+  sink (p);
+
+  p = memcpy (p + 3, s, 3);             // { dg-warning "writing 3 bytes into a region of size 0" }
+  sink (p);
+}
+
+
+void nowarn_mempcpy (const void *s)
+{
+  extern char a4[4];
+  unsigned n = sizeof a4;
+
+  char *p = mempcpy (a4, s, n);
+  sink (p);
+  mempcpy (p - 4, s, n);
+  sink (p);
+
+  p = mempcpy (a4 + 1, s, n - 1);
+  sink (p);
+  mempcpy (p - 4, s, n);
+  sink (p);
+
+  p = mempcpy (a4 + 2, s, n - 2);
+  sink (p);
+  mempcpy (p - 4, s, n);
+  sink (p);
+
+  p = mempcpy (a4 + 3, s, n - 3);
+  sink (p);
+  mempcpy (p - 4, s, n);
+  sink (p);
+
+  p = mempcpy (a4 + 4, s, n - 4);
+  sink (p);
+  mempcpy (p - 4, s, n);
+  sink (p);
+}
+
+
+void nowarn_mempcpy_chain (const void *s)
+{
+  extern char pcpy_a8[8];
+
+  char *p = pcpy_a8;
+
+  p = mempcpy (p + 1, s, 7);
+  sink (p);
+
+  p = mempcpy (p - 7 , s, 7);
+  sink (p);
+
+  p = mempcpy (p - 5 , s, 5);
+  sink (p);
+
+  p = mempcpy (p - 3 , s, 3);
+  sink (p);
+
+  p = mempcpy (p - 2 , s, 2);
+  sink (p);
+
+  mempcpy (p - 1, s, 1);
+  sink (p);
+
+  mempcpy (p - 8, s, 8);
+}
+
+
+void warn_mempcpy (const void *s)
+{
+  extern char pcpy_a5[5];               // { dg-message "destination object 'pcpy_a5'" "note" }
+
+  char *p = pcpy_a5;
+
+  p = mempcpy (p, s, 5);
+  sink (p);
+  mempcpy (p - 5, s, 6);                // { dg-warning "writing 6 bytes into a region of size 5 " }
+  sink (p);
+
+  p = pcpy_a5;
+  p = mempcpy (p, s, 3);
+  sink (p);
+  mempcpy (p, s, 3);                    // { dg-warning "writing 3 bytes into a region of size 2 " }
+  sink (p);
+
+  p = pcpy_a5 + 1;
+  p = mempcpy (p, s, 3);
+  sink (p);
+  mempcpy (p - 1, s, 5);                // { dg-warning "writing 5 bytes into a region of size 2 " }
+  sink (p);
+}
+
+
+void warn_mempcpy_chain_3 (const void *s)
+{
+  char *p = malloc (5);                 // { dg-message "at offset \\\[3, 5] into destination object of size 5" "note" }
+  p = mempcpy (p, s, UR (1, 2));
+  p = mempcpy (p, s, UR (2, 3));
+  p = mempcpy (p, s, UR (3, 4));        // { dg-warning "writing between 3 and 4 bytes into a region of size 2 " }
+
+  sink (p);
+}
+
+void warn_mempcpy_offrng_chain_3 (const void *s)
+{
+  char *p = malloc (11);                 // { dg-message "at offset \\\[9, 11] into destination object of size 11 " "note" }
+  size_t r1_2 = UR (1, 2);
+  size_t r2_3 = r1_2 + 1;
+  size_t r3_4 = r2_3 + 1;
+
+  p = mempcpy (p + r1_2, s, r1_2);
+  p = mempcpy (p + r2_3, s, r2_3);
+  p = mempcpy (p + r3_4, s, r3_4);       // { dg-warning "writing between 3 and 4 bytes into a region of size 2 " }
+
+  sink (p);
+}
+
+void warn_mempcpy_chain_4 (const void *s)
+{
+  char *p = malloc (9);                 // { dg-message "at offset \\\[6, 9] into destination object of size 9 " "note" }
+  p = mempcpy (p, s, UR (1, 2));
+  p = mempcpy (p, s, UR (2, 3));
+  p = mempcpy (p, s, UR (3, 4));
+  p = mempcpy (p, s, UR (4, 5));        // { dg-warning "writing between 4 and 5 bytes into a region of size 3 " }
+
+  sink (p);
+}
+
+void warn_mempcpy_chain_5 (const void *s)
+{
+  char *p = malloc (14);                // { dg-message "at offset \\\[10, 14] into destination object of size 14 " "note" }
+  p = mempcpy (p, s, UR (1, 2));
+  p = mempcpy (p, s, UR (2, 3));
+  p = mempcpy (p, s, UR (3, 4));
+  p = mempcpy (p, s, UR (4, 5));
+  p = mempcpy (p, s, UR (5, 6));        // { dg-warning "writing between 5 and 6 bytes into a region of size 4 " }
+
+  sink (p);
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-46.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-46.c
new file mode 100644 (file)
index 0000000..a4d78b2
--- /dev/null
@@ -0,0 +1,97 @@
+/* PR middle-end/97023 - missing warning on buffer overflow in chained mempcpy
+   Verify that out of bounds writes by built-ins to objects through pointers
+   returned by memchr() are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2" } */
+
+#include "range.h"
+
+void* malloc (size_t);
+void* memchr (void*, int, size_t);
+void* memset (void*, int, size_t);
+
+void sink (void*, ...);
+
+void nowarn_memchr_cst_memset_cst (const void *s)
+{
+  char *p = malloc (4);
+  sink (p);
+
+  p = memchr (p, '1', 4);
+  memset (p, 0, 4);
+}
+
+void nowarn_memchr_uint_memset_cst (const void *s, unsigned n)
+{
+  char *p = malloc (4);
+  sink (p);
+
+  p = memchr (p, '1', n);
+  memset (p, 0, 4);
+}
+
+void nowarn_memchr_sz_memset_cst (const void *s, size_t n)
+{
+  char *p = malloc (4);
+  sink (p);
+
+  p = memchr (p, '1', n);
+  memset (p, 0, 4);
+}
+
+void nowarn_memchr_anti_range_memset_cst (const void *s, size_t n)
+{
+  char *p = malloc (4);
+  sink (p);
+
+  if (n == 0)
+    n = 1;
+
+  p = memchr (p, '1', n);
+  memset (p, 0, 4);
+}
+
+void warn_memchr_cst_memset_cst (const void *s)
+{
+  char *p = malloc (4);                 // { dg-message "at offset \\\[0, 4] into destination object of size 4 " "note" }
+  sink (p);
+
+  p = memchr (p, '1', 4);
+  memset (p, 0, 5);                     // { dg-warning "writing 5 bytes into a region of size 4 " }
+}
+
+void warn_memchr_var_memset_cst (const void *s, unsigned n)
+{
+  char *p = malloc (4);                 // { dg-message "at offset \\\[0, 4] into destination object of size 4 " "note" }
+  sink (p);
+
+  p = memchr (p, '1', n);
+  memset (p, 0, 5);                     // { dg-warning "writing 5 bytes into a region of size 4 " }
+}
+
+void warn_memchr_var_memset_range (const void *s, unsigned n)
+{
+  /* The offsets in the first two notes are bounded by the size of
+     the allocated object.  The real upper bound of the offset in
+     the last note includes the upper bound f the offset of the pointer
+     returned from the previous memchr() call, but it ends up getting
+     constrained to the bounds of the allocated object so it's the same
+     as in the first two notes.  The exact value probably isn't too
+     important. */
+  char *p0 = malloc (UR (5, 7));
+  // { dg-message "at offset \\\[0, 7] into destination object of size \\\[5, 7]" "note" { target *-*-* } .-1 }
+  // { dg-message "at offset \\\[1, 7] into destination object of size \\\[5, 7]" "note"  { target *-*-* } .-2 }
+  // { dg-message "at offset \\\[2, 7] into destination object of size \\\[5, 7]" "note"  { target *-*-* } .-3 }
+
+  sink (p0);
+  char *p1 = memchr (p0, '1', n);
+  memset (p1, 0, UR (8, 9));            // { dg-warning "writing between 8 and 9 bytes into a region of size 7 " }
+
+  sink (p0);
+  p1 = memchr (p0 + 1, '2', n);
+  memset (p1, 0, UR (7, 9));            // { dg-warning "writing between 7 and 9 bytes into a region of size 6 " }
+
+  sink (p0);
+  char *p2 = memchr (p1 + 1, '3', n);
+  memset (p2, 0, UR (6, 9));            // { dg-warning "writing between 6 and 9 bytes into a region of size 5 " }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-47.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-47.c
new file mode 100644 (file)
index 0000000..02b14ee
--- /dev/null
@@ -0,0 +1,69 @@
+/* Verify that storing a bigger vector into smaller space is diagnosed.
+   { dg-do compile }
+   { dg-options "-O2" } */
+
+typedef __INT16_TYPE__                         int16_t;
+typedef __attribute__ ((__vector_size__ (32))) char C32;
+
+typedef __attribute__ ((__vector_size__ (64))) int16_t I16_64;
+
+void sink (void*);
+
+
+void nowarn_c32 (char c)
+{
+  extern char nowarn_a32[32];
+
+  void *p = nowarn_a32;
+  *(C32*)p = (C32){ c };
+  sink (p);
+
+  char a32[32];
+  p = a32;
+  *(C32*)p = (C32){ c };
+  sink (p);
+}
+
+void warn_c32 (char c)
+{
+  extern char warn_a32[32];   // { dg-message "at offset 32 to object 'warn_a32' with size 32" }
+
+  void *p = warn_a32 + 1;
+  *(C32*)p = (C32){ c };      // { dg-warning "writing 1 byte into a region of size 0" }
+
+  /* Verify a local variable too. */
+  char a32[32];
+  p = a32 + 1;
+  *(C32*)p = (C32){ c };      // { dg-warning "writing 1 byte into a region of size 0" }
+  sink (p);
+}
+
+
+void nowarn_i16_64 (int16_t i)
+{
+  extern char nowarn_a64[64];
+
+  void *p = nowarn_a64;
+  I16_64 *q = (I16_64*)p;
+  *q = (I16_64){ i };
+
+  char a64[64];
+  q = (I16_64*)a64;
+  *q = (I16_64){ i };
+  sink (q);
+}
+
+void warn_i16_64 (int16_t i)
+{
+  extern char warn_a64[64];   // { dg-message "at offset 128 to object 'warn_a64' with size 64" "pr97027" { xfail *-*-* } }
+
+  void *p = warn_a64 + 1;
+  I16_64 *q = (I16_64*)p;
+  *q = (I16_64){ i };         // { dg-warning "writing 1 byte into a region of size 0" "pr97027" { xfail *-*-* } }
+
+  char a64[64];
+  p = a64 + 1;
+  q = (I16_64*)p;
+  *q = (I16_64){ i };         // { dg-warning "writing 1 byte into a region of size 0" "pr97027" { xfail *-*-* } }
+  sink (p);
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-49.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-49.c
new file mode 100644 (file)
index 0000000..84b6c94
--- /dev/null
@@ -0,0 +1,146 @@
+/* Verify the handling of anti-ranges/multi-ranges by allocation functions
+   and subsequent accesses.
+   { dg-do compile }
+   { dg-options "-O2" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void* malloc (size_t);
+void  bzero (void*, size_t);
+void* memset (void*, int, size_t);
+
+
+/* Exercise size_t (via malloc and memset) and unsigned/signed int.  */
+
+__attribute__ ((alloc_size (1))) void*
+alloc_int (int);
+
+__attribute__ ((access (write_only, 1, 2))) void
+access_int (void*, int);
+
+__attribute__ ((alloc_size (1))) void*
+alloc_uint (unsigned);
+
+__attribute__ ((access (write_only, 1, 2))) void
+access_uint (void*, unsigned);
+
+
+void* nowarn_malloc_memset_same_anti_range (size_t n)
+{
+  /* Set N to the anti-range ~[3, 3].  */
+  if (n == 3)
+    n = 4;
+  void *p = malloc (n);
+
+  /* Verify there is no warning for an access to N bytes at P.
+     This means the warning has to assume the value of N in the call
+     to alloc() is in the larger subrange [4, UINT_MAX], while in
+     the call to access() in [0, 3].  */
+  return memset (p, 0, n);
+}
+
+/* Same as above but with two valid ranges.  */
+
+void* nowarn_malloc_memset_anti_range (size_t n1, size_t n2)
+{
+  /* Set N1 to the anti-range ~[3, 3].  */
+  if (n1 == 3)
+    n1 = 4;
+  void *p = malloc (n1);
+
+  /* Set N2 to the anti-range ~[7, 7].  */
+  if (n2 == 7)
+    n2 = 8;
+
+  return memset (p, 0, n2);
+}
+
+
+void nowarn_alloc_access_same_anti_range_int (int n)
+{
+  /* Set N to the anti-range ~[3, 3].  */
+  if (n == 3)
+    n = 4;
+  void *p = alloc_int (n);
+
+  /* Verify there is no warning for an access to N bytes at P.
+     This means the warning has to assume the value of N in the call
+     to alloc() is in the larger subrange [4, UINT_MAX], while in
+     the call to access() in [0, 3].  */
+  access_int (p, n);
+}
+
+/* Same as above but with two valid ranges.  */
+
+void nowarn_alloc_access_anti_range_int (int n1, int n2)
+{
+  /* Set N1 to the anti-range ~[3, 3].  */
+  if (n1 == 3)
+    n1 = 4;
+  void *p = alloc_int (n1);
+
+  /* Set N2 to the anti-range ~[7, 7].  */
+  if (n2 == 7)
+    n2 = 8;
+
+  access_int (p, n2);
+}
+
+
+void nowarn_alloc_access_same_anti_range_uint (unsigned n)
+{
+  /* Set N to the anti-range ~[3, 3].  */
+  if (n == 3)
+    n = 4;
+  void *p = alloc_uint (n);
+
+  /* Verify there is no warning for an access to N bytes at P.
+     This means the warning has to assume the value of N in the call
+     to alloc() is in the larger subrange [4, UINT_MAX], while in
+     the call to access() in [0, 3].  */
+  access_uint (p, n);
+}
+
+/* Same as above but with two valid ranges.  */
+
+void nowarn_alloc_access_anti_range_uint (unsigned n1, unsigned n2)
+{
+  /* Set N1 to the anti-range ~[3, 3].  */
+  if (n1 == 3)
+    n1 = 4;
+  void *p = alloc_uint (n1);
+
+  /* Set N2 to the anti-range ~[7, 7].  */
+  if (n2 == 7)
+    n2 = 8;
+
+  access_uint (p, n2);
+}
+
+
+void* nowarn_malloc_anti_range_memset_range (size_t n1, size_t n2)
+{
+  /* Set N1 to the anti-range ~[3, 3].  */
+  if (n1 == 3)
+    n1 = 4;
+  void *p = malloc (n1);
+
+  /* Set N2 to the range [5, MAX].  */
+  if (n2 < 5)
+    n2 = 5;
+  return memset (p, 0, n2);
+}
+
+void* nowarn_malloc_range_bzero_anti_range (size_t n1, size_t n2)
+{
+  /* Set N1 to the anti-range ~[3, 3].  */
+  if (n1 > 4)
+    n1 = 4;
+  void *p = malloc (n1);
+
+  /* Set N2 to the range [5, MAX].  */
+  if (n2 <= 3 || 5 <= n2)
+    n2 = 4;
+  bzero (p, n2);
+  return p;
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-50.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-50.c
new file mode 100644 (file)
index 0000000..7df58e5
--- /dev/null
@@ -0,0 +1,125 @@
+/* Verify that writes at excessive offsets into objects of unknown size
+   pointed to by function arguments are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2" } */
+
+#define DIFF_MAX __PTRDIFF_MAX__
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__    size_t;
+
+void* memset (void*, int, size_t);
+
+void sink (void*);
+
+char* fcall (void);
+
+void char_ptr_cst_off_cst_size (char *p)
+                                        // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'p'" "note" { target *-*-* } .-1 }
+{
+  size_t idx = DIFF_MAX - 3;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 2" }
+  sink (p);
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 1" }
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 0" }
+}
+
+
+void char_ptr_var_difoff_cst_size (ptrdiff_t idx)
+{
+  char *p = fcall ();
+  /* The offset is a range with a very large lower bound and an upper
+     bound of DIFF_MAX.  There's not point in also mentioning the latter
+     (it wouldn't make the note any more meaningful) so verify it only
+     mentions the lower bound.
+     { dg-message "at offset \\d+ into destination object of size \\\[0, \\d+] (allocated|returned) by 'fcall'" "note" { target *-*-* } .-5 } */
+
+  if (idx < DIFF_MAX - 3)
+    idx = DIFF_MAX - 3;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  memset (p + idx, 0, 5);               // { dg-warning "writing 5 bytes into a region of size 3" }
+}
+
+
+void char_ptr_var_szoff_cst_size (size_t idx)
+{
+  extern char* gptr;
+  // { dg-message "at offset \\d+ into destination object 'gptr'" "note" { target *-*-* } .-1 }
+
+  char *p = gptr;
+
+  if (idx < DIFF_MAX - 3)
+    idx = DIFF_MAX - 3;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  memset (p + idx, 0, 5);               // { dg-warning "writing 5 bytes into a region of size 3" "" { xfail *-*-* } }
+
+  if (idx > DIFF_MAX)
+    idx = DIFF_MAX;
+
+  memset (p + idx, 0, 7);               // { dg-warning "writing 7 bytes into a region of size 3" }
+}
+
+
+void char_ptr_var_difoff_var_size (char *p, ptrdiff_t idx, size_t n)
+                                        // { dg-message "at offset \\d+ into destination object 'p'" "note" { target *-*-* } .-1 }
+{
+  if (idx < DIFF_MAX - 3)
+    idx = DIFF_MAX - 3;
+
+  if (n < 3 || 7 < n)
+    n = 3;
+
+  memset (p + idx, 0, n);
+  sink (p);
+
+  ++n;
+  memset (p + idx, 0, n);               // { dg-warning "writing between 4 and 8 bytes into a region of size 3" }
+}
+
+
+void char_ptr_var_szoff_var_size (char *p, size_t idx, size_t n)
+                                        // { dg-message "at offset \\\[\[1-9\]\[0-9\]+, \[1-9\]\[0-9\]+] into destination object 'p'" "note" { xfail *-*-* } .-1 }
+{
+  if (idx < DIFF_MAX - 3)
+    idx = DIFF_MAX - 3;
+
+  if (n < 3 || 7 < n)
+    n = 3;
+
+  memset (p + idx, 0, n);
+  sink (p);
+
+  ++n;
+  /* With an unsigned offset large values are interpreted as negative
+     so the addition (p + idx) is effectively treated as subtraction,
+     making an overflow indistinguishable from a valid (if unlikely)
+     store.  */
+  memset (p + idx, 0, n);               // { dg-warning "writing between 4 and 8 bytes into a region of size 3" "pr?????" { xfail *-*-* } }
+}
+
+
+void int_ptr_cst_off_cst_size (int *p)
+                                        // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'p'" "note" { target *-*-* } .-1 }
+{
+  size_t idx = DIFF_MAX / sizeof *p;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  memset (p + idx, 0, 5);               // { dg-warning "writing 5 bytes into a region of size 3" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-51.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-51.c
new file mode 100644 (file)
index 0000000..6f36643
--- /dev/null
@@ -0,0 +1,34 @@
+/* Test case derived from Binutils/GDB's readline/readline/histexpand.c.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+char *
+get_subst_pattern (char *str, int *iptr, int delimiter, int is_rhs, int *lenptr)
+{
+  int si, i, j, k;
+  char *s;
+
+  s = 0;
+  i = *iptr;
+
+  for (si = i; str[si] && str[si] != delimiter; si++)
+      if (str[si] == '\\' && str[si + 1] == delimiter)
+       si++;
+
+  if (si > i || is_rhs)
+    {
+      s = (char *)__builtin_malloc (si - i + 1);
+      for (j = 0, k = i; k < si; j++, k++)
+       {
+         /* Remove a backslash quoting the search string delimiter. */
+         if (str[k] == '\\' && str[k + 1] == delimiter)
+           k++;
+         s[j] = str[k];   // { dg-bogus "-Wstringop-overflow" }
+       }
+      s[j] = '\0';
+      if (lenptr)
+       *lenptr = j;
+    }
+
+  return s;
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-52.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-52.c
new file mode 100644 (file)
index 0000000..a289655
--- /dev/null
@@ -0,0 +1,62 @@
+
+/* PR middle-end/97023 - missing warning on buffer overflow in chained mempcpy
+   Verify that writes by built-in functions to objects through pointers
+   returned by ordinary (non-built-int) function are assumed to point to
+   the beginning of objects.
+   { dg-do compile }
+   { dg-options "-O2" } */
+
+#include "range.h"
+
+void* memcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+
+void sink (void*, ...);
+
+extern char* arrptr[];
+extern char* ptr;
+extern char* retptr (void);
+struct S { char *p; };
+extern struct S retstruct (void);
+
+void nowarn_ptr (void)
+{
+  {
+    void *p = arrptr;
+    memset (p - 1, 0, 12345);           // { dg-warning "\\\[-Wstringop-overflow" }
+    memset (p,0, 12345);
+    memset (p,0, DIFF_MAX - 1);
+  }
+
+  {
+    char *p = arrptr[0];
+    memset (p - 1, 0, 12345);
+    memset (p - 12345, 0, 12345);
+    memset (p - 1234, 0, DIFF_MAX - 1);
+    memset (p - DIFF_MAX + 1, 0, 12345);
+  }
+
+  {
+    char *p = ptr;
+    memset (p - 1, 0, 12345);
+    memset (p - 12345, 0, 12345);
+    memset (p - 1234, 0, DIFF_MAX - 1);
+    memset (p - DIFF_MAX + 1, 0, 12345);
+  }
+
+  {
+    char *p = retptr ();
+    memset (p - 1, 0, 12345);
+    memset (p - 12345, 0, 12345);
+    memset (p - 1234, 0, DIFF_MAX - 1);
+    memset (p - DIFF_MAX + 1, 0, 12345);
+  }
+
+  {
+    char *p = retstruct ().p;
+    memset (p - 1, 0, 12345);
+    memset (p - 12345, 0, 12345);
+    memset (p - 1234, 0, DIFF_MAX - 1);
+    memset (p - DIFF_MAX + 1, 0, 12345);
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-53.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-53.c
new file mode 100644 (file)
index 0000000..cd8fa32
--- /dev/null
@@ -0,0 +1,116 @@
+/* PR middle-end/96384 - bogus -Wstringop-overflow= storing into
+   multidimensional array with index in range
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+#define SHRT_MAX   __SHRT_MAX__
+#define SHRT_MIN   (-SHRT_MAX - 1)
+#define INT_MAX    __INT_MAX__
+#define INT_MIN    (-INT_MAX - 1)
+#define LONG_MAX   __LONG_MAX__
+#define LONG_MIN   (-LONG_MAX - 1)
+
+#define USHRT_MAX  (SHRT_MAX * 2 + 1)
+#define UINT_MAX   ~0U
+#define ULONG_MAX  ~0LU
+
+char ca3_5_7[3][5][7];
+
+void nowarn_ca_3_5_ssi (short i)
+{
+  if (i > SHRT_MAX - 1)
+    i = SHRT_MAX - 1;
+
+  ca3_5_7[i][0][0] = __LINE__;
+  ca3_5_7[i][0][1] = __LINE__;
+  ca3_5_7[i][0][2] = __LINE__;
+  ca3_5_7[i][0][3] = __LINE__;
+  ca3_5_7[i][0][4] = __LINE__;
+  ca3_5_7[i][0][5] = __LINE__;
+  ca3_5_7[i][0][6] = __LINE__;
+
+  ca3_5_7[i][1][0] = __LINE__;
+  ca3_5_7[i][1][1] = __LINE__;
+  ca3_5_7[i][1][2] = __LINE__;
+  ca3_5_7[i][1][3] = __LINE__;
+  ca3_5_7[i][1][4] = __LINE__;
+  ca3_5_7[i][1][5] = __LINE__;
+  ca3_5_7[i][1][6] = __LINE__;
+
+  ca3_5_7[i][2][0] = __LINE__;
+  ca3_5_7[i][2][1] = __LINE__;
+  ca3_5_7[i][2][2] = __LINE__;
+  ca3_5_7[i][2][3] = __LINE__;
+  ca3_5_7[i][2][4] = __LINE__;
+  ca3_5_7[i][2][5] = __LINE__;
+  ca3_5_7[i][2][6] = __LINE__;
+
+  ca3_5_7[i][3][0] = __LINE__;
+  ca3_5_7[i][3][1] = __LINE__;
+  ca3_5_7[i][3][2] = __LINE__;
+  ca3_5_7[i][3][3] = __LINE__;
+  ca3_5_7[i][3][4] = __LINE__;
+  ca3_5_7[i][3][5] = __LINE__;
+  ca3_5_7[i][3][6] = __LINE__;
+
+  ca3_5_7[i][4][0] = __LINE__;
+  ca3_5_7[i][4][1] = __LINE__;
+  ca3_5_7[i][4][2] = __LINE__;
+  ca3_5_7[i][4][3] = __LINE__;
+  ca3_5_7[i][4][4] = __LINE__;
+  ca3_5_7[i][4][5] = __LINE__;
+  ca3_5_7[i][4][6] = __LINE__;
+
+  ca3_5_7[1][i][5] = __LINE__;
+  ca3_5_7[2][3][i] = __LINE__;
+}
+
+void nowarn_ca_3_5_usi (unsigned short i)
+{
+  if (i > USHRT_MAX - 1)
+    i = USHRT_MAX - 1;
+
+  ca3_5_7[i][3][5] = __LINE__;
+  ca3_5_7[1][i][5] = __LINE__;
+  ca3_5_7[2][3][i] = __LINE__;
+}
+
+void nowarn_ca_3_5_si (int i)
+{
+  if (i > INT_MAX - 1)
+    i = INT_MAX - 1;
+
+  ca3_5_7[i][3][5] = __LINE__;
+  ca3_5_7[1][i][5] = __LINE__;
+  ca3_5_7[2][3][i] = __LINE__;
+}
+
+void nowarn_ca_3_5_ui (unsigned i)
+{
+  if (i > UINT_MAX - 1)
+    i = UINT_MAX - 1;
+
+  ca3_5_7[i][3][5] = __LINE__;
+  ca3_5_7[1][i][5] = __LINE__;
+  ca3_5_7[2][3][i] = __LINE__;
+}
+
+void nowarn_ca_3_5_li (long i)
+{
+  if (i > LONG_MAX - 1)
+    i = LONG_MAX - 1;
+
+  ca3_5_7[i][3][5] = __LINE__;
+  ca3_5_7[1][i][5] = __LINE__;
+  ca3_5_7[2][3][i] = __LINE__;
+}
+
+void nowarn_ca_3_5_uli (unsigned long i)
+{
+  if (i > ULONG_MAX - 1)
+    i = ULONG_MAX - 1;
+
+  ca3_5_7[i][3][5] = __LINE__;
+  ca3_5_7[1][i][5] = __LINE__;
+  ca3_5_7[2][3][i] = __LINE__;
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-54.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-54.c
new file mode 100644 (file)
index 0000000..26568f8
--- /dev/null
@@ -0,0 +1,103 @@
+/* Verify that writes at excessive offsets into flexible array members
+   of extern or allocated objects of unknow size are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2" } */
+
+#define DIFF_MAX __PTRDIFF_MAX__
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__    size_t;
+
+void* memset (void*, int, size_t);
+
+void sink (void*);
+
+void char_flexarray_cst_off_cst_size (void)
+{
+  extern struct { char n, a[]; }
+    caxcc;                              // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'caxcc'" }
+
+  char *p = caxcc.a;
+  size_t idx = DIFF_MAX - 4;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 2" }
+  sink (p);
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 1" }
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 0" }
+}
+
+
+void char_flexarray_var_off_cst_size (ptrdiff_t idx)
+{
+  extern struct { char n, a[]; }
+    caxvc;                              // { dg-message "destination object 'caxvc'" }
+
+  char *p = caxvc.a;
+
+  if (idx < DIFF_MAX - 4)
+    idx = DIFF_MAX - 4;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  memset (p + idx, 0, 5);               // { dg-warning "writing 5 bytes into a region of size 3" }
+}
+
+
+void char_flexarray_var_off_var_size (size_t n, ptrdiff_t idx)
+{
+  extern struct { char n, a[]; }
+    caxvv;                              // { dg-message "destination object 'caxvv'" }
+
+  char *p = caxvv.a;
+
+  if (idx < DIFF_MAX - 4)
+    idx = DIFF_MAX - 4;
+
+  if (n < 3 || 7 < n)
+    n = 3;
+
+  memset (p + idx, 0, n);
+  sink (p);
+
+  ++n;
+  memset (p + idx, 0, n);               // { dg-warning "writing between 4 and 8 bytes into a region of size 3" }
+}
+
+
+void alloc_array_var_off_cst_size (size_t n, ptrdiff_t idx)
+{
+  struct { char n, a[]; }
+    *p = __builtin_malloc (n);          // { dg-message "at offset \\d+ into destination object" }
+
+  if (idx < DIFF_MAX - 4)
+    idx = DIFF_MAX - 4;
+
+  memset (p->a + idx, 0, 3);
+  sink (p);
+
+  memset (p->a + idx, 0, 5);            // { dg-warning "writing 5 bytes into a region of size 3" }
+}
+
+
+void int_array_cst_off_cst_size (void)
+{
+  extern struct { int n, a[]; }
+    iaxc;                               // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'iaxc'" }
+
+  int *p = iaxc.a;
+  size_t idx = DIFF_MAX / sizeof *p - 1;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  memset (p + idx, 0, 5);               // { dg-warning "writing 5 bytes into a region of size 3" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-55.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-55.c
new file mode 100644 (file)
index 0000000..25f5b82
--- /dev/null
@@ -0,0 +1,97 @@
+/* Verify that offsets in "anti-ranges" are handled correctly.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__    size_t;
+
+void* memset (void*, int, size_t);
+
+void sink (void*, ...);
+#define T(x) sink (x)
+
+
+void int_range_add_sub_ (int i, int j)
+{
+  if (i < 1) i = 1;
+  if (j > -1) j = -1;
+
+  char ca5[5];              // { dg-message "at offset \\\[1, 5]" "note" }
+  char *p0 = ca5;           // offset
+  char *p1 = p0 + i;        //  1-5
+  char *p2 = p1 + i;        //  2-5
+  char *p3 = p2 + j;        //  0-4
+  char *p4 = p3 + j;        //  0-3
+  char *p5 = p4 + j;        //  0-2
+  char *p6 = p5 + j;        //  0-1
+  char *p7 = p6 + i;        //  1-2
+
+  memset (p7, 0, 5);        // { dg-warning "writing 5 bytes into a region of size 4" }
+
+  sink (p0, p1, p2, p3, p4, p5, p6, p7);
+}
+
+
+void ruint_arint_add (unsigned i, int j)
+{
+  i |= 1;   // [1, UINT_MAX]
+  j |= 1;   // [INT_MIN + 1, -1] U [1, INT_MAX]
+
+  char a[5];                // { dg-message "at offset \\\[1, 5]" "note" }
+  char *p0 = a;             // offset
+  char *p1 = p0 + i;        //  1-5
+  T (memset (p1, 0, 4));
+
+  char *p2 = p1 + j;        //  0-5
+  T (memset (p2, 0, 5));
+
+  char *p3 = p2 + i;        //  1-5
+  T (memset (p3, 0, 4));
+
+  char *p4 = p3 + j;        //  0-5
+  T (memset (p4, 0, 5));
+
+  char *p5 = p4 + i;        //  1-5
+  T (memset (p5, 0, 4));
+
+  char *p6 = p5 + j;        //  0-5
+  T (memset (p6, 0, 5));
+
+  char *p7 = p6 + i;        //  1-5
+  T (memset (p7, 0, 5));    // { dg-warning "writing 5 bytes into a region of size 4" "" }
+}
+
+
+void warn_ptrdiff_anti_range_add (ptrdiff_t i)
+{
+  i |= 1;
+
+  char ca5[5];              // { dg-message "at offset \\\[1, 5]" "pr?????" { xfail *-*-* } }
+  char *p0 = ca5;           // offset
+  char *p1 = p0 + i;        //  1-5
+  char *p2 = p1 + i;        //  2-5
+  char *p3 = p2 + i;        //  3-5
+  char *p4 = p3 + i;        //  4-5
+  char *p5 = p4 + i;        //   5
+
+  memset (p5, 0, 5);        // { dg-warning "writing 5 bytes into a region of size 0" "pr?????" { xfail *-*-* } }
+
+  sink (p0, p1, p2, p3, p4, p5);
+}
+
+void warn_int_anti_range (int i)
+{
+  i |= 1;
+
+  char ca5[5];              // { dg-message "at offset \\\[1, 5]" "pr?????" { xfail *-*-* } }
+  char *p0 = ca5;           // offset
+  char *p1 = p0 + i;        //  1-5
+  char *p2 = p1 + i;        //  2-5
+  char *p3 = p2 + i;        //  3-5
+  char *p4 = p3 + i;        //  4-5
+  char *p5 = p4 + i;        //   5
+
+  memset (p5, 0, 5);        // { dg-warning "writing 5 bytes into a region of size 0" "pr?????" { xfail *-*-* } }
+
+  sink (p0, p1, p2, p3, p4, p5);
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overread-5.c b/gcc/testsuite/gcc.dg/Wstringop-overread-5.c
new file mode 100644 (file)
index 0000000..b75002b
--- /dev/null
@@ -0,0 +1,76 @@
+/* Verify -Wstringop-overread with a source pointer pointing either
+   before the beginning or past the end of an object.
+   { dg-do compile }
+   { dg-options "-O -Wall -Wno-array-bounds" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+size_t strlen (const char *);
+
+void sink (void*, ...);
+
+void off_sz_or_1 (size_t i)
+{
+  i |= 1;
+
+  /* Verify the offset in the notes only mentions the meaningful lower
+     bound and not a range with the excessive (and meaningless) upper
+     bound like [2, 9223372036854775807].  */
+  extern char a[1];
+  // { dg-message "at offset 1 into source object 'a'" "note" { target *-*-* } .-1 }
+  // { dg-message "at offset 2 " "note" { target *-*-* } .-2 }
+
+  char *p1 = a + i;
+  char *p2 = p1 + 1;
+  char *p3 = p1 - 1;
+
+  size_t n = 0;
+  n += strlen (p1);     // { dg-warning "reading 1 or more bytes from a region of size 0" }
+  n += strlen (p2);     // { dg-warning "reading 1 or more bytes from a region of size 0" }
+  n += strlen (p3);
+
+  sink (p1, p2, p3, n);
+}
+
+
+void off_sz_or_2 (size_t i)
+{
+  i |= 2;
+
+  extern char b[2];
+  // { dg-message "at offset 2 " "note" { target *-*-* } .-1 }
+  // { dg-message "at offset 3 " "note" { target *-*-* } .-2 }
+
+  char *p1 = b + i;
+  char *p2 = p1 + 1;
+  char *p3 = p1 - 1;
+
+  size_t n = 0;
+  n += strlen (p1);     // { dg-warning "reading 1 or more bytes from a region of size 0" }
+  n += strlen (p2);     // { dg-warning "reading 1 or more bytes from a region of size 0" }
+  n += strlen (p3);
+
+  sink (p1, p2, p3, n);
+}
+
+
+void off_sz_or_4 (size_t i)
+{
+  i |= 4;
+
+  extern char c[3];
+  // { dg-message "at offset 4 " "note" { target *-*-* } .-1 }
+  // { dg-message "at offset 5 " "note" { target *-*-* } .-2 }
+  // { dg-message "at offset 3 " "note" { target *-*-* } .-3 }
+
+  char *p1 = c + i;
+  char *p2 = p1 + 1;
+  char *p3 = p1 - 1;
+
+  size_t n = 0;
+  n += strlen (p1);     // { dg-warning "reading 1 or more bytes from a region of size 0" }
+  n += strlen (p2);     // { dg-warning "reading 1 or more bytes from a region of size 0" }
+  n += strlen (p3);     // { dg-warning "reading 1 or more bytes from a region of size 0" }
+
+  sink (p1, p2, p3, n);
+}
index c477cd8fdd205fd2a6764d4b1a2467408cbad577..7a624e4055d1622e295586e29796c9f16c69d9c1 100644 (file)
@@ -12,6 +12,9 @@ void *
 foo (void *p)
 {
   return bar ((void *) 0x12345000, p, 256);
+  /* Integers converted to pointers are assumed to be the result of
+     (invalid) arithmetic on null pointers.
+     { dg-prune-output "writing 256 bytes into a region of size 0" } */
 }
 
 /* { dg-final { scan-tree-dump "memcpy" "optimized" } } */
index e6033a783f433a9f70844bcb8eb2b153ed5a4f82..962c705f18f1be67407bec3b6e53beb8ab54effb 100644 (file)
@@ -11,3 +11,6 @@ foo (void)
   __builtin_memcpy (buf, (void *) 0x8000, 4096);
   bar (buf);
 }
+
+/* Reading from a constant address might triggers:
+   { dg-prune-output "\\\[-Wstringop-overread" } */
index f4d1c5ca25625713651ba1f44faf64030f28b7f9..ebb17cd852c87a6323f64d326858a144963556a1 100644 (file)
@@ -228,8 +228,20 @@ get_range (tree val, gimple *stmt, wide_int minmax[2],
 
   value_range_kind rng = get_range_info (val, minmax, minmax + 1);
   if (rng == VR_RANGE)
+    /* This may be an inverted range whose MINMAX[1] < MINMAX[0].  */
     return val;
 
+  if (rng == VR_ANTI_RANGE)
+    {
+      /* An anti-range is the same as an ordinary range with inverted
+        bounds (where MINMAX[1] < MINMAX[0] is true) that may result
+        from the conversion of a signed anti-range to unsigned.  */
+      wide_int tmp = minmax[0];
+      minmax[0] = minmax[1] + 1;
+      minmax[1] = wi::sub (tmp, 1);
+      return val;
+    }
+
   /* Do not handle anti-ranges and instead make use of the on-demand
      VRP if/when it becomes available (hopefully in GCC 11).  */
   return NULL_TREE;
@@ -2243,7 +2255,7 @@ maybe_warn_overflow (gimple *stmt, tree len,
     sprintf (offstr, "[%lli, %lli]",
             (long long) offrng[0].to_shwi (), (long long) offrng[1].to_shwi ());
 
-  if (destdecl)
+  if (destdecl && DECL_P (destdecl))
     {
       if (tree size = DECL_SIZE_UNIT (destdecl))
        inform (DECL_SOURCE_LOCATION (destdecl),