From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1930) id 06A9B3858401; Tue, 24 Aug 2021 16:49:51 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 06A9B3858401 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Martin Sebor To: gcc-cvs@gcc.gnu.org Subject: [gcc r12-3124] Reset PHI base0 flag if it's clear in any argument [PR101977, ...] X-Act-Checkin: gcc X-Git-Author: Martin Sebor X-Git-Refname: refs/heads/master X-Git-Oldrev: 6d692ef43b2b3368c92c3fb757c7884fc94ee627 X-Git-Newrev: 820f0940d7ace1306430a9dcf1bd9577508a7a7e Message-Id: <20210824164952.06A9B3858401@sourceware.org> Date: Tue, 24 Aug 2021 16:49:51 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 24 Aug 2021 16:49:52 -0000 https://gcc.gnu.org/g:820f0940d7ace1306430a9dcf1bd9577508a7a7e commit r12-3124-g820f0940d7ace1306430a9dcf1bd9577508a7a7e Author: Martin Sebor Date: Tue Aug 24 10:49:11 2021 -0600 Reset PHI base0 flag if it's clear in any argument [PR101977, ...] 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. Diff: --- gcc/gimple-ssa-warn-access.cc | 20 +++- gcc/pointer-query.cc | 105 ++++++++++++--------- gcc/testsuite/g++.dg/pr100574.C | 4 + gcc/testsuite/g++.dg/warn/Warray-bounds-26.C | 27 ++++++ gcc/testsuite/gcc.dg/Warray-bounds-88.c | 134 +++++++++++++++++++++++++++ gcc/testsuite/gcc.dg/Wstringop-overread.c | 32 +++---- gcc/testsuite/gcc.dg/pr20126.c | 6 ++ gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c | 39 ++++---- 8 files changed, 286 insertions(+), 81 deletions(-) diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc index 4a2dd9ade77..5df97a6473a 100644 --- a/gcc/gimple-ssa-warn-access.cc +++ b/gcc/gimple-ssa-warn-access.cc @@ -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)) { diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc index 99caf78bfa7..ba8f8a9dc69 100644 --- a/gcc/pointer-query.cc +++ b/gcc/pointer-query.cc @@ -634,10 +634,10 @@ access_ref::phi () const return as_a (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 *all_refs, @@ -659,21 +659,25 @@ access_ref::get_ref (vec *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 *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 *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 *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 *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; diff --git a/gcc/testsuite/g++.dg/pr100574.C b/gcc/testsuite/g++.dg/pr100574.C index 42ba0404a28..0df62aa70ad 100644 --- a/gcc/testsuite/g++.dg/pr100574.C +++ b/gcc/testsuite/g++.dg/pr100574.C @@ -40,6 +40,8 @@ template template 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 index 00000000000..f72ac9d4b51 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-26.C @@ -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(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 index 00000000000..8cee8d28571 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Warray-bounds-88.c @@ -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; +} diff --git a/gcc/testsuite/gcc.dg/Wstringop-overread.c b/gcc/testsuite/gcc.dg/Wstringop-overread.c index 0343e43cce4..7db74029819 100644 --- a/gcc/testsuite/gcc.dg/Wstringop-overread.c +++ b/gcc/testsuite/gcc.dg/Wstringop-overread.c @@ -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)); } diff --git a/gcc/testsuite/gcc.dg/pr20126.c b/gcc/testsuite/gcc.dg/pr20126.c index a421ce1758d..10aeec74f39 100644 --- a/gcc/testsuite/gcc.dg/pr20126.c +++ b/gcc/testsuite/gcc.dg/pr20126.c @@ -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" } */ diff --git a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c index 2afd2b5feeb..846e9300750 100644 --- a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c +++ b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c @@ -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]; };