]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Reset PHI base0 flag if it's clear in any argument [PR101977, ...]
authorMartin Sebor <msebor@redhat.com>
Tue, 24 Aug 2021 16:49:11 +0000 (10:49 -0600)
committerMartin Sebor <msebor@redhat.com>
Tue, 24 Aug 2021 16:49:11 +0000 (10:49 -0600)
Resolves:
PR middle-end/101600 - Spurious -Warray-bounds downcasting a polymorphic pointer
PR middle-end/101977 - bogus -Warray-bounds on a negative index into a parameter in conditional with null

gcc/ChangeLog:

PR middle-end/101600
PR middle-end/101977
* gimple-ssa-warn-access.cc (maybe_warn_for_bound): Tighten up
the phrasing of a warning.
(check_access): Use the remaining size after subtracting any offset
rather than the whole object size.
* pointer-query.cc (access_ref::get_ref): Clear BASE0 flag if it's
clear for any nonnull PHI argument.
(compute_objsize): Clear argument.

gcc/testsuite/ChangeLog:

PR middle-end/101600
PR middle-end/101977
* g++.dg/pr100574.C: Prune out valid warning.
* gcc.dg/pr20126.c: Same.
* gcc.dg/Wstringop-overread.c: Adjust text of expected warnings.
Add new instances.
* gcc.dg/warn-strnlen-no-nul.c: Same.
* g++.dg/warn/Warray-bounds-26.C: New test.
* gcc.dg/Warray-bounds-88.c: New test.

gcc/gimple-ssa-warn-access.cc
gcc/pointer-query.cc
gcc/testsuite/g++.dg/pr100574.C
gcc/testsuite/g++.dg/warn/Warray-bounds-26.C [new file with mode: 0644]
gcc/testsuite/gcc.dg/Warray-bounds-88.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overread.c
gcc/testsuite/gcc.dg/pr20126.c
gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c

index 4a2dd9ade77d1fac9b3b53cd6844ec4bc274b194..5df97a6473a02f38bdbae076b129963a6b31af54 100644 (file)
@@ -704,6 +704,15 @@ maybe_warn_for_bound (opt_code opt, location_t loc, GimpleOrTree exp, tree func,
   if (opt == OPT_Wstringop_overread)
     {
       bool maybe = pad && pad->src.phi ();
+      if (maybe)
+       {
+         /* Issue a "maybe" warning only if the PHI refers to objects
+            at least one of which has more space remaining than the bound.
+            Otherwise, if the bound is greater, use the definitive form.  */
+         offset_int remmax = pad->src.size_remaining ();
+         if (remmax < wi::to_offset (bndrng[0]))
+           maybe = false;
+       }
 
       if (tree_int_cst_lt (maxobjsize, bndrng[0]))
        {
@@ -788,6 +797,15 @@ maybe_warn_for_bound (opt_code opt, location_t loc, GimpleOrTree exp, tree func,
     }
 
   bool maybe = pad && pad->dst.phi ();
+  if (maybe)
+    {
+      /* Issue a "maybe" warning only if the PHI refers to objects
+        at least one of which has more space remaining than the bound.
+        Otherwise, if the bound is greater, use the definitive form.  */
+      offset_int remmax = pad->dst.size_remaining ();
+      if (remmax < wi::to_offset (bndrng[0]))
+       maybe = false;
+    }
   if (tree_int_cst_lt (maxobjsize, bndrng[0]))
     {
       if (bndrng[0] == bndrng[1])
@@ -1418,7 +1436,7 @@ check_access (GimpleOrTree exp, tree dstwrite,
       location_t loc = get_location (exp);
       tree size = dstsize;
       if (pad && pad->mode == access_read_only)
-       size = wide_int_to_tree (sizetype, pad->src.sizrng[1]);
+       size = wide_int_to_tree (sizetype, pad->src.size_remaining ());
 
       if (range[0] && maxread && tree_fits_uhwi_p (size))
        {
index 99caf78bfa7c715f925114ada9efb646451b2e9f..ba8f8a9dc69b316703b5fc924e2402478d867fe8 100644 (file)
@@ -634,10 +634,10 @@ access_ref::phi () const
   return as_a <gphi *> (def_stmt);
 }
 
-/* Determine and return the largest object to which *THIS.  If *THIS
-   refers to a PHI and PREF is nonnull, fill *PREF with the details
-   of the object determined by compute_objsize(ARG, OSTYPE) for each
-   PHI argument ARG.  */
+/* Determine and return the largest object to which *THIS refers.  If
+   *THIS refers to a PHI and PREF is nonnull, fill *PREF with the details
+   of the object determined by compute_objsize(ARG, OSTYPE) for each PHI
+   argument ARG.  */
 
 tree
 access_ref::get_ref (vec<access_ref> *all_refs,
@@ -659,21 +659,25 @@ access_ref::get_ref (vec<access_ref> *all_refs,
   if (!psnlim->visit_phi (ref))
     return NULL_TREE;
 
-  /* Reflects the range of offsets of all PHI arguments refer to the same
-     object (i.e., have the same REF).  */
-  access_ref same_ref;
-  /* The conservative result of the PHI reflecting the offset and size
-     of the largest PHI argument, regardless of whether or not they all
-     refer to the same object.  */
   pointer_query empty_qry;
   if (!qry)
     qry = &empty_qry;
 
+  /* The conservative result of the PHI reflecting the offset and size
+     of the largest PHI argument, regardless of whether or not they all
+     refer to the same object.  */
   access_ref phi_ref;
   if (pref)
     {
+      /* The identity of the object has not been determined yet but
+        PREF->REF is set by the caller to the PHI for convenience.
+        The size is negative/invalid and the offset is zero (it's
+        updated only after the identity of the object has been
+        established).  */
+      gcc_assert (pref->sizrng[0] < 0);
+      gcc_assert (pref->offrng[0] == 0 && pref->offrng[1] == 0);
+
       phi_ref = *pref;
-      same_ref = *pref;
     }
 
   /* Set if any argument is a function array (or VLA) parameter not
@@ -682,8 +686,6 @@ access_ref::get_ref (vec<access_ref> *all_refs,
   /* The size of the smallest object referenced by the PHI arguments.  */
   offset_int minsize = 0;
   const offset_int maxobjsize = wi::to_offset (max_object_size ());
-  /* The offset of the PHI, not reflecting those of its arguments.  */
-  const offset_int orng[2] = { phi_ref.offrng[0], phi_ref.offrng[1] };
 
   const unsigned nargs = gimple_phi_num_args (phi_stmt);
   for (unsigned i = 0; i < nargs; ++i)
@@ -695,28 +697,31 @@ access_ref::get_ref (vec<access_ref> *all_refs,
        /* A PHI with all null pointer arguments.  */
        return NULL_TREE;
 
-      /* Add PREF's offset to that of the argument.  */
-      phi_arg_ref.add_offset (orng[0], orng[1]);
       if (TREE_CODE (arg) == SSA_NAME)
        qry->put_ref (arg, phi_arg_ref);
 
       if (all_refs)
        all_refs->safe_push (phi_arg_ref);
 
-      const bool arg_known_size = (phi_arg_ref.sizrng[0] != 0
-                                  || phi_arg_ref.sizrng[1] != maxobjsize);
-
       parmarray |= phi_arg_ref.parmarray;
 
       const bool nullp = integer_zerop (arg) && (i || i + 1 < nargs);
 
       if (phi_ref.sizrng[0] < 0)
        {
+         /* If PHI_REF doesn't contain a meaningful result yet set it
+            to the result for the first argument.  */
          if (!nullp)
-           same_ref = phi_arg_ref;
-         phi_ref = phi_arg_ref;
+           phi_ref = phi_arg_ref;
+
+         /* Set if the current argument refers to one or more objects of
+            known size (or range of sizes), as opposed to referring to
+            one or more unknown object(s).  */
+         const bool arg_known_size = (phi_arg_ref.sizrng[0] != 0
+                                      || phi_arg_ref.sizrng[1] != maxobjsize);
          if (arg_known_size)
            minsize = phi_arg_ref.sizrng[0];
+
          continue;
        }
 
@@ -740,8 +745,10 @@ access_ref::get_ref (vec<access_ref> *all_refs,
       offset_int phirem[2];
       phirem[1] = phi_ref.size_remaining (phirem);
 
-      if (phi_arg_ref.ref != same_ref.ref)
-       same_ref.ref = NULL_TREE;
+      /* Reset the PHI's BASE0 flag if any of the nonnull arguments
+        refers to an object at an unknown offset.  */
+      if (!phi_arg_ref.base0)
+       phi_ref.base0 = false;
 
       if (phirem[1] < argrem[1]
          || (phirem[1] == argrem[1]
@@ -749,32 +756,13 @@ access_ref::get_ref (vec<access_ref> *all_refs,
        /* Use the argument with the most space remaining as the result,
           or the larger one if the space is equal.  */
        phi_ref = phi_arg_ref;
-
-      /* Set SAME_REF.OFFRNG to the maximum range of all arguments.  */
-      if (phi_arg_ref.offrng[0] < same_ref.offrng[0])
-       same_ref.offrng[0] = phi_arg_ref.offrng[0];
-      if (same_ref.offrng[1] < phi_arg_ref.offrng[1])
-       same_ref.offrng[1] = phi_arg_ref.offrng[1];
     }
 
-  if (!same_ref.ref && same_ref.offrng[0] != 0)
-    /* Clear BASE0 if not all the arguments refer to the same object and
-       if not all their offsets are zero-based.  This allows the final
-       PHI offset to out of bounds for some arguments but not for others
-       (or negative even of all the arguments are BASE0), which is overly
-       permissive.  */
-    phi_ref.base0 = false;
-
-  if (same_ref.ref)
-    phi_ref = same_ref;
-  else
-    {
-      /* Replace the lower bound of the largest argument with the size
-        of the smallest argument, and set PARMARRAY if any argument
-        was one.  */
-      phi_ref.sizrng[0] = minsize;
-      phi_ref.parmarray = parmarray;
-    }
+  /* Replace the lower bound of the largest argument with the size
+     of the smallest argument, and set PARMARRAY if any argument
+     was one.  */
+  phi_ref.sizrng[0] = minsize;
+  phi_ref.parmarray = parmarray;
 
   if (phi_ref.sizrng[0] < 0)
     {
@@ -804,6 +792,14 @@ access_ref::size_remaining (offset_int *pmin /* = NULL */) const
   if (!pmin)
     pmin = &minbuf;
 
+  if (sizrng[0] < 0)
+    {
+      /* If the identity of the object hasn't been determined return
+        the maximum size range.  */
+      *pmin = 0;
+      return wi::to_offset (max_object_size ());
+    }
+
   /* add_offset() ensures the offset range isn't inverted.  */
   gcc_checking_assert (offrng[0] <= offrng[1]);
 
@@ -1597,6 +1593,11 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
     {
       pref->ref = ptr;
 
+      /* Reset the offset in case it was set by a prior call and not
+        cleared by the caller.  The offset is only adjusted after
+        the identity of the object has been determined.  */
+      pref->offrng[0] = pref->offrng[1] = 0;
+
       if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr)))
        {
          /* Set the maximum size if the reference is to the pointer
@@ -1607,6 +1608,9 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
          return true;
        }
 
+      /* Valid offsets into the object are nonnegative.  */
+      pref->base0 = true;
+
       if (tree size = decl_init_size (ptr, false))
        if (TREE_CODE (size) == INTEGER_CST)
          {
@@ -1960,6 +1964,11 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
 {
   pointer_query qry;
   qry.rvals = rvals;
+
+  /* Clear and invalidate in case *PREF is being reused.  */
+  pref->offrng[0] = pref->offrng[1] = 0;
+  pref->sizrng[0] = pref->sizrng[1] = -1;
+
   ssa_name_limit_t snlim;
   if (!compute_objsize_r (ptr, ostype, pref, snlim, &qry))
     return NULL_TREE;
@@ -1982,6 +1991,10 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, pointer_query *ptr_qry)
   else
     ptr_qry = &qry;
 
+  /* Clear and invalidate in case *PREF is being reused.  */
+  pref->offrng[0] = pref->offrng[1] = 0;
+  pref->sizrng[0] = pref->sizrng[1] = -1;
+
   ssa_name_limit_t snlim;
   if (!compute_objsize_r (ptr, ostype, pref, snlim, ptr_qry))
     return NULL_TREE;
index 42ba0404a28e94a2d98b499a88801eeddd8309df..0df62aa70ad28423b3baa3e9bbc2cefaa8553129 100644 (file)
@@ -40,6 +40,8 @@ template <typename _Tp, typename _Alloc>
 template <typename...>
 void vector<_Tp, _Alloc>::_M_realloc_insert() {
   __alloc_traits::pointer __trans_tmp_5;
+  /* __len is used uninitialized below, which might trigger warnings,
+     even without -Wall (and other than -Wuninitialized).  */
   long __len(__len || max_size()), __elems_before;
   __trans_tmp_5 = _M_allocate___n
     ? __alloc_traits::allocate(_M_impl, _M_allocate___n)
@@ -62,3 +64,5 @@ void ReadTrackChunk()
     case MIDIST_PITCHBEND:
       block.data.push_back();
 }
+
+// { dg-prune-output "warning" }
diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-26.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-26.C
new file mode 100644 (file)
index 0000000..f72ac9d
--- /dev/null
@@ -0,0 +1,27 @@
+/* PR middle-end/101600 - Spurious -Warray-bounds downcasting a polymorphic
+   pointer
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+struct S1 { virtual ~S1(); };
+struct S2 { int m; };
+struct S3 { virtual ~S3(); };
+struct S4: S1, S2, S3 {};
+
+int f1 ();
+
+void f2 (S3 *);
+
+void f3 (S2 *p)
+{
+  for (int i = f1 (); f1 (); )
+    {
+      if (i == 0)
+       {
+         p = 0;
+         break;
+       }
+    }
+
+  f2 (static_cast<S4 *>(p));  // { dg-bogus "-Warray-bounds" }
+}
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-88.c b/gcc/testsuite/gcc.dg/Warray-bounds-88.c
new file mode 100644 (file)
index 0000000..8cee8d2
--- /dev/null
@@ -0,0 +1,134 @@
+/* PR middle-end/101977 - bogus -Warray-bounds on a negative index into
+   a parameter in conditional with null
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+struct A { int i; };
+struct B { struct A a1; struct A a2; };
+
+
+void nowarn_p_0_0 (struct A *p, int i)
+{
+  struct A *q = i < 0 ? p : 0 < i ? (struct A*)0 : 0;
+  struct B *r = (struct B*)((char *)q - __builtin_offsetof (struct B, a2));
+  r->a1.i = 0;
+}
+
+void nowarn_0_p_0 (struct A *p, int i)
+{
+  struct A *q = i < 0 ? 0 : 0 < i ? p : 0;
+  struct B *r = (struct B*)((char *)q - __builtin_offsetof (struct B, a2));
+  r->a1.i = 0;      // { dg-bogus "-Warray-bounds" }
+}
+
+void nowarn_0_0_p (struct A *p, int i)
+{
+  struct A *q = i < 0 ? 0 : 0 < i ? 0 : p;
+  struct B *r = (struct B*)((char *)q - __builtin_offsetof (struct B, a2));
+  r->a1.i = 0;      // { dg-bogus "-Warray-bounds" }
+}
+
+
+void nowarn_p_q_0 (struct A *p, struct A *q, int i)
+{
+  struct A *r = i < 0 ? p : 0 < i ? q : 0;
+  struct B *s = (struct B*)((char *)r - __builtin_offsetof (struct B, a2));
+  s->a1.i = 0;      // { dg-bogus "-Warray-bounds" }
+}
+
+void nowarn_p_0_q (struct A *p, struct A *q, int i)
+{
+  struct A *r = i < 0 ? p : 0 < i ? 0 : q;
+  struct B *s = (struct B*)((char *)r - __builtin_offsetof (struct B, a2));
+  s->a1.i = 0;      // { dg-bogus "-Warray-bounds" }
+}
+
+void nowarn_0_p_q (struct A *p, struct A *q, int i)
+{
+  struct A *r = i < 0 ? 0 : 0 < i ? p : q;
+  struct B *s = (struct B*)((char *)r - __builtin_offsetof (struct B, a2));
+  s->a1.i = 0;
+}
+
+
+void nowarn_p_q_r (struct A *p, struct A *q, struct A *r, int i)
+{
+  struct A *s = i < 0 ? p : 0 < i ? q : r;
+  struct B *t = (struct B*)((char *)s - __builtin_offsetof (struct B, a2));
+  t->a1.i = 0;
+}
+
+
+extern struct B b1, b2, b3;
+
+void nowarn_p_b1_0 (struct A *p, int i)
+{
+  struct A *r = i < 0 ? p : 0 < i ? &b1.a2 : 0;
+  struct B *s = (struct B*)((char *)r - __builtin_offsetof (struct B, a2));
+  s->a1.i = 0;      // { dg-bogus "-Warray-bounds" }
+}
+
+void nowarn_p_0_b1 (struct A *p, int i)
+{
+  struct A *r = i < 0 ? p : 0 < i ? 0 : &b1.a2;
+  struct B *s = (struct B*)((char *)r - __builtin_offsetof (struct B, a2));
+  s->a1.i = 0;      // { dg-bogus "-Warray-bounds" }
+}
+
+void nowarn_0_p_b1 (struct A *p, int i)
+{
+  struct A *r = i < 0 ? 0 : 0 < i ? p : &b1.a2;
+  struct B *s = (struct B*)((char *)r - __builtin_offsetof (struct B, a2));
+  s->a1.i = 0;
+}
+
+
+void nowarn_p_b1_b2 (struct A *p, int i)
+{
+  struct A *s = i < 0 ? p : 0 < i ? &b1.a2 : &b2.a2;
+  struct B *t = (struct B*)((char *)s - __builtin_offsetof (struct B, a2));
+  t->a1.i = 0;
+}
+
+void nowarn_b1_p_b2 (struct A *p, int i)
+{
+  struct A *s = i < 0 ? &b1.a2 : 0 < i ? p : &b2.a2;
+  struct B *t = (struct B*)((char *)s - __builtin_offsetof (struct B, a2));
+  t->a1.i = 0;
+}
+
+void nowarn_b1_b2_p (struct A *p, int i)
+{
+  struct A *s = i < 0 ? &b1.a2 : 0 < i ? &b2.a2 : p;
+  struct B *t = (struct B*)((char *)s - __builtin_offsetof (struct B, a2));
+  t->a1.i = 0;
+}
+
+void nowarn_b1_b2_b3 (struct A *p, int i)
+{
+  struct A *s = i < 0 ? &b1.a2 : 0 < i ? &b2.a2 : &b3.a2;
+  struct B *t = (struct B*)((char *)s - __builtin_offsetof (struct B, a2));
+  t->a1.i = 0;
+}
+
+
+void nowarn_0_b1_b2 (int i)
+{
+  struct A *s = i < 0 ? 0 : 0 < i ? &b1.a2 : &b2.a2;
+  struct B *t = (struct B*)((char *)s - __builtin_offsetof (struct B, a2));
+  t->a1.i = 0;
+}
+
+void warn_b1_0_b2 (int i)
+{
+  struct A *s = i < 0 ? &b1.a2 : 0 < i ? 0 : &b2.a2;
+  struct B *t = (struct B*)((char *)s - __builtin_offsetof (struct B, a2));
+  t->a1.i = 0;
+}
+
+void warn_b1_b2_0 (int i)
+{
+  struct A *s = i < 0 ? &b1.a2 : 0 < i ? &b2.a2 : 0;
+  struct B *t = (struct B*)((char *)s - __builtin_offsetof (struct B, a2));
+  t->a1.i = 0;
+}
index 0343e43cce47e937ca216dc5bf41a9ce0ca45014..7db74029819225e4c697503603c49dbaef0db87c 100644 (file)
@@ -317,9 +317,9 @@ void test_strnlen_array (int i, int i0, unsigned n)
   T (strnlen (a1, n));
 
   T (strnlen (a1 + 1, 0));
-  T (strnlen (a1 + 1, 1));      // { dg-warning "'strnlen' reading 1 byte from a region of size 0" }
+  T (strnlen (a1 + 1, 1));      // { dg-warning "'strnlen' specified bound 1 exceeds source size 0" }
   T (strnlen (a1 + 1, i0));
-  T (strnlen (a1 + 1, i0 + 1)); // { dg-warning "'strnlen' reading between 1 and \[0-9\]+ bytes from a region of size 0" }
+  T (strnlen (a1 + 1, i0 + 1)); // { dg-warning "'strnlen' specified bound \\\[1, \\d+] exceeds source size 0" }
   T (strnlen (a1 + 1, n));
   T (strnlen (a1 + i, 0));
   T (strnlen (a1 + i, 1));
@@ -335,7 +335,7 @@ void test_strnlen_array (int i, int i0, unsigned n)
   T (strnlen (a1 + i0, 2));     // { dg-warning "'strnlen' specified bound 2 exceeds source size 1" }
   T (strnlen (a1 + i0, n));
   T (strnlen (a1 + i0 + 1, 0));
-  T (strnlen (a1 + i0 + 1, 1)); // { dg-warning "'strnlen' reading 1 byte from a region of size 0" }
+  T (strnlen (a1 + i0 + 1, 1)); // { dg-warning "'strnlen' specified bound 1 exceeds source size 0" }
   T (strnlen (a1 + i0 + 1, n));
 
   T (strnlen (a2, 0));
@@ -344,10 +344,10 @@ void test_strnlen_array (int i, int i0, unsigned n)
   T (strnlen (a2, n));
   T (strnlen (a2 + 1, 0));
   T (strnlen (a2 + 1, 1));
-  T (strnlen (a2 + 1, 2));      // { dg-warning "'strnlen' specified bound 2 exceeds source size 1"  "pr87492" { xfail *-*-* } }
+  T (strnlen (a2 + 1, 2));      // { dg-warning "'strnlen' specified bound 2 exceeds source size 1"  "pr87492" }
   T (strnlen (a2 + 1, n));
   T (strnlen (a2 + 2, 0));
-  T (strnlen (a2 + 2, 1));      // { dg-warning "'strnlen' reading 1 byte from a region of size 0" }
+  T (strnlen (a2 + 2, 1));      // { dg-warning "'strnlen' specified bound 1 exceeds source size 0" }
   T (strnlen (a2 + 2, n));
   T (strnlen (a2 + i, 0));
   T (strnlen (a2 + i, 1));
@@ -365,13 +365,13 @@ void test_strnlen_array (int i, int i0, unsigned n)
 
   T (strnlen (a2 + i0 + 1, 0));
   T (strnlen (a2 + i0 + 1, 1));
-  T (strnlen (a2 + i0 + 1, 2));
+  T (strnlen (a2 + i0 + 1, 2)); // { dg-warning "'strnlen' specified bound 2 exceeds source size 1" }
   T (strnlen (a2 + i0 + 1, n));
 
   T (strnlen (a2 + i0 + 2, 0));
-  T (strnlen (a2 + i0 + 2, 1)); // { dg-warning "'strnlen' reading 1 byte from a region of size 0" }
+  T (strnlen (a2 + i0 + 2, 1)); // { dg-warning "'strnlen' specified bound 1 exceeds source size 0" }
   T (strnlen (a2 + i0 + 2, i0));
-  T (strnlen (a2 + i0 + 2, i0 + 1)); // { dg-warning "'strnlen' reading between 1 and \[0-9\]+ bytes from a region of size 0" }
+  T (strnlen (a2 + i0 + 2, i0 + 1)); // { dg-warning "'strnlen' specified bound \\\[1, \\d+] exceeds source size 0" }
   T (strnlen (a2 + i0 + 2, n));
 }
 
@@ -512,9 +512,9 @@ void test_strndup_array (int i, int i0, unsigned n)
   T (strndup (a1, 2));          // { dg-warning "'strndup' specified bound 2 exceeds source size 1" }
   T (strndup (a1, n));
   T (strndup (a1 + 1, 0));
-  T (strndup (a1 + 1, 1));      // { dg-warning "'strndup' reading 1 byte from a region of size 0" }
+  T (strndup (a1 + 1, 1));      // { dg-warning "'strndup' specified bound 1 exceeds source size 0" }
   T (strndup (a1 + 1, i0));
-  T (strndup (a1 + 1, i0 + 1)); // { dg-warning "'strndup' reading between 1 and \[0-9\]+ bytes from a region of size 0" }
+  T (strndup (a1 + 1, i0 + 1)); // { dg-warning "'strndup' specified bound \\\[1, \\d+] exceeds source size 0" }
   T (strndup (a1 + 1, n));
   T (strndup (a1 + i, 0));
   T (strndup (a1 + i, 1));
@@ -529,7 +529,7 @@ void test_strndup_array (int i, int i0, unsigned n)
   T (strndup (a1 + i0, 1));
   T (strndup (a1 + i0, n));
   T (strndup (a1 + i0 + 1, 0));
-  T (strndup (a1 + i0 + 1, 1)); // { dg-warning "'strndup' reading 1 byte from a region of size 0" }
+  T (strndup (a1 + i0 + 1, 1)); // { dg-warning "'strndup' specified bound 1 exceeds source size 0" }
   T (strndup (a1 + i0 + 1, n));
 
   T (strndup (a2, 0));
@@ -538,10 +538,10 @@ void test_strndup_array (int i, int i0, unsigned n)
   T (strndup (a2, n));
   T (strndup (a2 + 1, 0));
   T (strndup (a2 + 1, 1));
-  T (strndup (a2 + 1, 2));
+  T (strndup (a2 + 1, 2));      // { dg-warning "'strndup' specified bound 2 exceeds source size 1" }
   T (strndup (a2 + 1, n));
   T (strndup (a2 + 2, 0));
-  T (strndup (a2 + 2, 1));      // { dg-warning "'strndup' reading 1 byte from a region of size 0" }
+  T (strndup (a2 + 2, 1));      // { dg-warning "'strndup' specified bound 1 exceeds source size 0" }
   T (strndup (a2 + 2, n));
   T (strndup (a2 + i, 0));
   T (strndup (a2 + i, 1));
@@ -559,13 +559,13 @@ void test_strndup_array (int i, int i0, unsigned n)
 
   T (strndup (a2 + i0 + 1, 0));
   T (strndup (a2 + i0 + 1, 1));
-  T (strndup (a2 + i0 + 1, 2));
+  T (strndup (a2 + i0 + 1, 2)); // { dg-warning "'strndup' specified bound 2 exceeds source size 1" }
   T (strndup (a2 + i0 + 1, n));
 
   T (strndup (a2 + i0 + 2, 0));
-  T (strndup (a2 + i0 + 2, 1)); // { dg-warning "'strndup' reading 1 byte from a region of size 0" }
+  T (strndup (a2 + i0 + 2, 1)); // { dg-warning "'strndup' specified bound 1 exceeds source size 0" }
   T (strndup (a2 + i0 + 2, i0));
-  T (strndup (a2 + i0 + 2, i0 + 1)); // { dg-warning "'strndup' reading between 1 and \[0-9\]+ bytes from a region of size 0" }
+  T (strndup (a2 + i0 + 2, i0 + 1)); // { dg-warning "'strndup' specified bound \\\[1, \\d+] exceeds source size 0" }
   T (strndup (a2 + i0 + 2, n));
 }
 
index a421ce1758ddd265793784511dd809e873c0e8b4..10aeec74f396090be3472a5d52a9bd3a99162db7 100644 (file)
@@ -34,6 +34,10 @@ foo (S *x, S *y)
   while (e <= g)
     {
       const char *t = e + 1;
+      /* The pointer E below increases but the bound H stays constant,
+        letting the latter exceed the size remaining in the argument
+        pointed to by the formed, which might be detected by
+        -Wstringop-overread.  */
       if (__builtin_memcmp (e, f, h) == 0)
         return 1;
       e = t;
@@ -48,3 +52,5 @@ main (void)
     abort ();
   return 0;
 }
+
+/* { dg-prune-output "-Wstringop-overread" } */
index 2afd2b5feeb351fa54ff8d67cc9c824ec4395297..846e9300750e29181619e07cb6850ada7ef81597 100644 (file)
@@ -143,14 +143,17 @@ T (v0 ? b[1] : "", bsz);
 T (v0 ? b[2] : "", bsz);
 T (v0 ? b[3] : "", bsz);
 
-T (v0 ? "" : b[0], bsz + 1);      /* { dg-warning "bound 6 may exceed source size 5" } */
-T (v0 ? "" : b[1], bsz + 1);
-T (v0 ? "" : b[2], bsz + 1);
-T (v0 ? "" : b[3], bsz + 1);      /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
-T (v0 ? b[0] : "", bsz + 1);      /* { dg-warning "bound 6 may exceed source size 5" } */
-T (v0 ? b[1] : "", bsz + 1);
-T (v0 ? b[2] : "", bsz + 1);
-T (v0 ? b[3] : "", bsz + 1);      /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
+/* The warnings below are strictly correct but the strnlen calls are safe
+   because the reads are bounded by the length of the constant arguments.
+   It might make sense to relax the warning to avoid triggering for them.  */
+T (v0 ? "" : b[0], bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? "" : b[1], bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? "" : b[2], bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? "" : b[3], bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[0] : "", bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[1] : "", bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[2] : "", bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[3] : "", bsz + 1);      /* { dg-warning "bound 6 exceeds source size 5" } */
 
 T (v0 ? "" : b[i0], bsz);
 T (v0 ? "" : b[i1], bsz);
@@ -164,11 +167,11 @@ T (v0 ? b[i3] : "", bsz);
 T (v0 ? "" : b[i0], bsz + 1);
 T (v0 ? "" : b[i1], bsz + 1);
 T (v0 ? "" : b[i2], bsz + 1);
-T (v0 ? "" : b[i3], bsz + 1);     /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
+T (v0 ? "" : b[i3], bsz + 1);     /* { dg-warning "bound 6 exceeds source size 5" "pr86937" } */
 T (v0 ? b[i0] : "", bsz + 1);
 T (v0 ? b[i1] : "", bsz + 1);
 T (v0 ? b[i2] : "", bsz + 1);
-T (v0 ? b[i3] : "", bsz + 1);     /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
+T (v0 ? b[i3] : "", bsz + 1);     /* { dg-warning "bound 6 exceeds source size 5" "pr86937" } */
 
 T (v0 ? "1234" : b[3], bsz);
 T (v0 ? "1234" : b[i3], bsz);
@@ -180,15 +183,15 @@ T (v0 ? b[0] : b[2], bsz);
 T (v0 ? b[2] : b[3], bsz);
 T (v0 ? b[3] : b[2], bsz);
 
-T (v0 ? "1234" : b[3], bsz + 1);  /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
-T (v0 ? "1234" : b[i3], bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
-T (v0 ? b[3] : "1234", bsz + 1);  /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
-T (v0 ? b[i3] : "1234", bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
+T (v0 ? "1234" : b[3], bsz + 1);  /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? "1234" : b[i3], bsz + 1); /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[3] : "1234", bsz + 1);  /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[i3] : "1234", bsz + 1); /* { dg-warning "bound 6 exceeds source size 5" } */
 
-T (v0 ? a : b[3], bsz + 1);       /* { dg-warning "bound 6 may exceed source size 5" "pr86937" { xfail *-*-*} } */
-T (v0 ? b[0] : b[2], bsz + 1);    /* { dg-warning "bound 6 may exceed source size 5" "pr86937" } */
-T (v0 ? b[2] : b[3], bsz + 1);    /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
-T (v0 ? b[3] : b[2], bsz + 1);    /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
+T (v0 ? a : b[3], bsz + 1);       /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[0] : b[2], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[2] : b[3], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" } */
+T (v0 ? b[3] : b[2], bsz + 1);    /* { dg-warning "bound 6 exceeds source size 5" } */
 
 struct A { char a[5], b[5]; };