diff mbox series

separate reading past the end from -Wstringop-overflow

Message ID 36cbf657-d2ab-0b45-e3a0-cce86cbfe5a2@gmail.com
State New
Headers show
Series separate reading past the end from -Wstringop-overflow | expand

Commit Message

Martin Sebor June 24, 2020, 2:05 a.m. UTC
-Wstringop-overflow is issued for both writing and reading past
the end, even though the latter problem is often viewed as being
lower severity than the former, or at least sufficiently different
to triage separately.  In CWE, for example, each of the two kinds
of problems has its own classification (CWE-787: Out-of-bounds
Write, and CWE-125: Out-of-bounds Read).

To make this easier with GCC, the attached patch introduces
a new option called -Wstringop-overread and splits the current
indiscriminate warning into the respective two categories.  Other
than the new option the improvements also exposed a few instances
of reading past the end that GCC doesn't detect that the new code
does thanks to more consistent checking.

As usual, I tested the patch by building Binutils/GDB, Glibc, and
the Linux kernel with no unexpected (or any, in fact) instances
of the two warnings.

The work involved more churn that I expected, and maintaining
the expected precedence of warnings (missing terminating nul,
excessive bounds, etc.) turned out to be more delicate and time
consuming than I would have liked.  I think the result is cleaner
code, but I'm quite sure it can still stand to be made better.
That's also my plan for the next step when I'd like to move this
checking out of builtins.c and calls.c and into a file of its own.
Ultimately, Jeff and have would like to integrate it into the path
isolation pass.

Martin

PS There's a FIXME comment in expand_builtin_memory_chk as
a reminder of a future change.  It currently has no bearing
on anything.

Comments

Martin Sebor July 7, 2020, 6:56 p.m. UTC | #1
Ping.  Despite its size, there isn't much new in the patch, it
pretty much just splits an existing warning into two, one for
buffer overflow and another for "overread."

https://gcc.gnu.org/pipermail/gcc-patches/2020-June/548786.html

On 6/23/20 8:05 PM, Martin Sebor wrote:
> -Wstringop-overflow is issued for both writing and reading past
> the end, even though the latter problem is often viewed as being
> lower severity than the former, or at least sufficiently different
> to triage separately.  In CWE, for example, each of the two kinds
> of problems has its own classification (CWE-787: Out-of-bounds
> Write, and CWE-125: Out-of-bounds Read).
> 
> To make this easier with GCC, the attached patch introduces
> a new option called -Wstringop-overread and splits the current
> indiscriminate warning into the respective two categories.  Other
> than the new option the improvements also exposed a few instances
> of reading past the end that GCC doesn't detect that the new code
> does thanks to more consistent checking.
> 
> As usual, I tested the patch by building Binutils/GDB, Glibc, and
> the Linux kernel with no unexpected (or any, in fact) instances
> of the two warnings.
> 
> The work involved more churn that I expected, and maintaining
> the expected precedence of warnings (missing terminating nul,
> excessive bounds, etc.) turned out to be more delicate and time
> consuming than I would have liked.  I think the result is cleaner
> code, but I'm quite sure it can still stand to be made better.
> That's also my plan for the next step when I'd like to move this
> checking out of builtins.c and calls.c and into a file of its own.
> Ultimately, Jeff and have would like to integrate it into the path
> isolation pass.
> 
> Martin
> 
> PS There's a FIXME comment in expand_builtin_memory_chk as
> a reminder of a future change.  It currently has no bearing
> on anything.
Martin Sebor July 14, 2020, 12:05 a.m. UTC | #2
Ping: https://gcc.gnu.org/pipermail/gcc-patches/2020-June/548786.html

On 7/7/20 12:56 PM, Martin Sebor wrote:
> Ping.  Despite its size, there isn't much new in the patch, it
> pretty much just splits an existing warning into two, one for
> buffer overflow and another for "overread."
> 
> https://gcc.gnu.org/pipermail/gcc-patches/2020-June/548786.html
> 
> On 6/23/20 8:05 PM, Martin Sebor wrote:
>> -Wstringop-overflow is issued for both writing and reading past
>> the end, even though the latter problem is often viewed as being
>> lower severity than the former, or at least sufficiently different
>> to triage separately.  In CWE, for example, each of the two kinds
>> of problems has its own classification (CWE-787: Out-of-bounds
>> Write, and CWE-125: Out-of-bounds Read).
>>
>> To make this easier with GCC, the attached patch introduces
>> a new option called -Wstringop-overread and splits the current
>> indiscriminate warning into the respective two categories.  Other
>> than the new option the improvements also exposed a few instances
>> of reading past the end that GCC doesn't detect that the new code
>> does thanks to more consistent checking.
>>
>> As usual, I tested the patch by building Binutils/GDB, Glibc, and
>> the Linux kernel with no unexpected (or any, in fact) instances
>> of the two warnings.
>>
>> The work involved more churn that I expected, and maintaining
>> the expected precedence of warnings (missing terminating nul,
>> excessive bounds, etc.) turned out to be more delicate and time
>> consuming than I would have liked.  I think the result is cleaner
>> code, but I'm quite sure it can still stand to be made better.
>> That's also my plan for the next step when I'd like to move this
>> checking out of builtins.c and calls.c and into a file of its own.
>> Ultimately, Jeff and have would like to integrate it into the path
>> isolation pass.
>>
>> Martin
>>
>> PS There's a FIXME comment in expand_builtin_memory_chk as
>> a reminder of a future change.  It currently has no bearing
>> on anything.
>
Martin Sebor July 26, 2020, 5:42 p.m. UTC | #3
Ping: https://gcc.gnu.org/pipermail/gcc-patches/2020-June/548786.html

On 7/13/20 6:05 PM, Martin Sebor wrote:
> Ping: https://gcc.gnu.org/pipermail/gcc-patches/2020-June/548786.html
> 
> On 7/7/20 12:56 PM, Martin Sebor wrote:
>> Ping.  Despite its size, there isn't much new in the patch, it
>> pretty much just splits an existing warning into two, one for
>> buffer overflow and another for "overread."
>>
>> https://gcc.gnu.org/pipermail/gcc-patches/2020-June/548786.html
>>
>> On 6/23/20 8:05 PM, Martin Sebor wrote:
>>> -Wstringop-overflow is issued for both writing and reading past
>>> the end, even though the latter problem is often viewed as being
>>> lower severity than the former, or at least sufficiently different
>>> to triage separately.  In CWE, for example, each of the two kinds
>>> of problems has its own classification (CWE-787: Out-of-bounds
>>> Write, and CWE-125: Out-of-bounds Read).
>>>
>>> To make this easier with GCC, the attached patch introduces
>>> a new option called -Wstringop-overread and splits the current
>>> indiscriminate warning into the respective two categories.  Other
>>> than the new option the improvements also exposed a few instances
>>> of reading past the end that GCC doesn't detect that the new code
>>> does thanks to more consistent checking.
>>>
>>> As usual, I tested the patch by building Binutils/GDB, Glibc, and
>>> the Linux kernel with no unexpected (or any, in fact) instances
>>> of the two warnings.
>>>
>>> The work involved more churn that I expected, and maintaining
>>> the expected precedence of warnings (missing terminating nul,
>>> excessive bounds, etc.) turned out to be more delicate and time
>>> consuming than I would have liked.  I think the result is cleaner
>>> code, but I'm quite sure it can still stand to be made better.
>>> That's also my plan for the next step when I'd like to move this
>>> checking out of builtins.c and calls.c and into a file of its own.
>>> Ultimately, Jeff and have would like to integrate it into the path
>>> isolation pass.
>>>
>>> Martin
>>>
>>> PS There's a FIXME comment in expand_builtin_memory_chk as
>>> a reminder of a future change.  It currently has no bearing
>>> on anything.
>>
>
Martin Sebor Aug. 10, 2020, 4:48 p.m. UTC | #4
Ping: https://gcc.gnu.org/pipermail/gcc-patches/2020-June/548786.html

On 7/26/20 11:42 AM, Martin Sebor wrote:
> Ping: https://gcc.gnu.org/pipermail/gcc-patches/2020-June/548786.html
> 
> On 7/13/20 6:05 PM, Martin Sebor wrote:
>> Ping: https://gcc.gnu.org/pipermail/gcc-patches/2020-June/548786.html
>>
>> On 7/7/20 12:56 PM, Martin Sebor wrote:
>>> Ping.  Despite its size, there isn't much new in the patch, it
>>> pretty much just splits an existing warning into two, one for
>>> buffer overflow and another for "overread."
>>>
>>> https://gcc.gnu.org/pipermail/gcc-patches/2020-June/548786.html
>>>
>>> On 6/23/20 8:05 PM, Martin Sebor wrote:
>>>> -Wstringop-overflow is issued for both writing and reading past
>>>> the end, even though the latter problem is often viewed as being
>>>> lower severity than the former, or at least sufficiently different
>>>> to triage separately.  In CWE, for example, each of the two kinds
>>>> of problems has its own classification (CWE-787: Out-of-bounds
>>>> Write, and CWE-125: Out-of-bounds Read).
>>>>
>>>> To make this easier with GCC, the attached patch introduces
>>>> a new option called -Wstringop-overread and splits the current
>>>> indiscriminate warning into the respective two categories.  Other
>>>> than the new option the improvements also exposed a few instances
>>>> of reading past the end that GCC doesn't detect that the new code
>>>> does thanks to more consistent checking.
>>>>
>>>> As usual, I tested the patch by building Binutils/GDB, Glibc, and
>>>> the Linux kernel with no unexpected (or any, in fact) instances
>>>> of the two warnings.
>>>>
>>>> The work involved more churn that I expected, and maintaining
>>>> the expected precedence of warnings (missing terminating nul,
>>>> excessive bounds, etc.) turned out to be more delicate and time
>>>> consuming than I would have liked.  I think the result is cleaner
>>>> code, but I'm quite sure it can still stand to be made better.
>>>> That's also my plan for the next step when I'd like to move this
>>>> checking out of builtins.c and calls.c and into a file of its own.
>>>> Ultimately, Jeff and have would like to integrate it into the path
>>>> isolation pass.
>>>>
>>>> Martin
>>>>
>>>> PS There's a FIXME comment in expand_builtin_memory_chk as
>>>> a reminder of a future change.  It currently has no bearing
>>>> on anything.
>>>
>>
>
Martin Sebor Aug. 24, 2020, 7:05 p.m. UTC | #5
Ping: https://gcc.gnu.org/pipermail/gcc-patches/2020-June/548786.html

On 8/10/20 10:48 AM, Martin Sebor wrote:
> Ping: https://gcc.gnu.org/pipermail/gcc-patches/2020-June/548786.html
> 
> On 7/26/20 11:42 AM, Martin Sebor wrote:
>> Ping: https://gcc.gnu.org/pipermail/gcc-patches/2020-June/548786.html
>>
>> On 7/13/20 6:05 PM, Martin Sebor wrote:
>>> Ping: https://gcc.gnu.org/pipermail/gcc-patches/2020-June/548786.html
>>>
>>> On 7/7/20 12:56 PM, Martin Sebor wrote:
>>>> Ping.  Despite its size, there isn't much new in the patch, it
>>>> pretty much just splits an existing warning into two, one for
>>>> buffer overflow and another for "overread."
>>>>
>>>> https://gcc.gnu.org/pipermail/gcc-patches/2020-June/548786.html
>>>>
>>>> On 6/23/20 8:05 PM, Martin Sebor wrote:
>>>>> -Wstringop-overflow is issued for both writing and reading past
>>>>> the end, even though the latter problem is often viewed as being
>>>>> lower severity than the former, or at least sufficiently different
>>>>> to triage separately.  In CWE, for example, each of the two kinds
>>>>> of problems has its own classification (CWE-787: Out-of-bounds
>>>>> Write, and CWE-125: Out-of-bounds Read).
>>>>>
>>>>> To make this easier with GCC, the attached patch introduces
>>>>> a new option called -Wstringop-overread and splits the current
>>>>> indiscriminate warning into the respective two categories.  Other
>>>>> than the new option the improvements also exposed a few instances
>>>>> of reading past the end that GCC doesn't detect that the new code
>>>>> does thanks to more consistent checking.
>>>>>
>>>>> As usual, I tested the patch by building Binutils/GDB, Glibc, and
>>>>> the Linux kernel with no unexpected (or any, in fact) instances
>>>>> of the two warnings.
>>>>>
>>>>> The work involved more churn that I expected, and maintaining
>>>>> the expected precedence of warnings (missing terminating nul,
>>>>> excessive bounds, etc.) turned out to be more delicate and time
>>>>> consuming than I would have liked.  I think the result is cleaner
>>>>> code, but I'm quite sure it can still stand to be made better.
>>>>> That's also my plan for the next step when I'd like to move this
>>>>> checking out of builtins.c and calls.c and into a file of its own.
>>>>> Ultimately, Jeff and have would like to integrate it into the path
>>>>> isolation pass.
>>>>>
>>>>> Martin
>>>>>
>>>>> PS There's a FIXME comment in expand_builtin_memory_chk as
>>>>> a reminder of a future change.  It currently has no bearing
>>>>> on anything.
>>>>
>>>
>>
>
Li, Pan2 via Gcc-patches Aug. 26, 2020, 8:36 p.m. UTC | #6
On Tue, 2020-06-23 at 20:05 -0600, Martin Sebor wrote:
> -Wstringop-overflow is issued for both writing and reading past
> the end, even though the latter problem is often viewed as being
> lower severity than the former, or at least sufficiently different
> to triage separately.  In CWE, for example, each of the two kinds
> of problems has its own classification (CWE-787: Out-of-bounds
> Write, and CWE-125: Out-of-bounds Read).
> 
> To make this easier with GCC, the attached patch introduces
> a new option called -Wstringop-overread and splits the current
> indiscriminate warning into the respective two categories.  Other
> than the new option the improvements also exposed a few instances
> of reading past the end that GCC doesn't detect that the new code
> does thanks to more consistent checking.
> 
> As usual, I tested the patch by building Binutils/GDB, Glibc, and
> the Linux kernel with no unexpected (or any, in fact) instances
> of the two warnings.
> 
> The work involved more churn that I expected, and maintaining
> the expected precedence of warnings (missing terminating nul,
> excessive bounds, etc.) turned out to be more delicate and time
> consuming than I would have liked.  I think the result is cleaner
> code, but I'm quite sure it can still stand to be made better.
> That's also my plan for the next step when I'd like to move this
> checking out of builtins.c and calls.c and into a file of its own.
> Ultimately, Jeff and have would like to integrate it into the path
> isolation pass.
> 
> Martin
> 
> PS There's a FIXME comment in expand_builtin_memory_chk as
> a reminder of a future change.  It currently has no bearing
> on anything.

> Add -Wstringop-overread for reading past the end by string functions.
> 
> gcc/ChangeLog:
> 
> 	* attribs.c (init_attr_rdwr_indices): Use global access_mode.
> 	* attribs.h (struct attr_access): Same.
> 	* builtins.c (fold_builtin_strlen): Add argument.
> 	(compute_objsize): Declare.
> 	(get_range): Declare.
> 	(check_read_access): New function.
> 	(access_ref::access_ref): Define ctor.
> 	(warn_string_no_nul): Add arguments.  Handle -Wstrintop-overread.
> 	(check_nul_terminated_array): Handle source strings of different
> 	ranges of sizes.
> 	(expand_builtin_strlen): Remove warning code, call check_read_access
> 	instead.  Declare locals closer to their initialization.
> 	(expand_builtin_strnlen): Same.
> 	(maybe_warn_for_bound): New function.
> 	(warn_for_access): Remove argument.  Handle -Wstrintop-overread.
> 	(inform_access): Change argument type.
> 	(get_size_range): New function.
> 	(check_access): Remove unused arguments.  Add new arguments.  Handle
> 	-Wstrintop-overread.  Move warning code to helpers and call them.
> 	Call check_nul_terminated_array.
> 	(check_memop_access): Remove unnecessary and provide additional
> 	arguments in calls.
> 	(expand_builtin_memchr): Call check_read_access.
> 	(expand_builtin_strcat): Remove unnecessary and provide additional
> 	arguments in calls.
> 	(expand_builtin_strcpy): Same.
> 	(expand_builtin_strcpy_args): Same.  Avoid testing no-warning bit.
> 	(expand_builtin_stpcpy_1): Remove unnecessary and provide additional
> 	arguments in calls.
> 	(expand_builtin_stpncpy): Same.
> 	(check_strncat_sizes): Same.
> 	(expand_builtin_strncat): Remove unnecessary and provide additional
> 	arguments in calls.  Adjust comments.
> 	(expand_builtin_strncpy): Remove unnecessary and provide additional
> 	arguments in calls.
> 	(expand_builtin_memcmp): Remove warning code.  Call check_access.
> 	(expand_builtin_strcmp): Call check_access instead of
> 	check_nul_terminated_array.
> 	(expand_builtin_strncmp): Handle -Wstrintop-overread.
> 	(expand_builtin_fork_or_exec): Call check_access instead of
> 	check_nul_terminated_array.
> 	(expand_builtin): Same.
> 	(fold_builtin_1): Pass additional argument.
> 	(fold_builtin_n): Same.
> 	(fold_builtin_strpbrk): Remove calls to check_nul_terminated_array.
> 	(expand_builtin_memory_chk): Add comments.
> 	(maybe_emit_chk_warning): Remove unnecessary and provide additional
> 	arguments in calls.
> 	(maybe_emit_sprintf_chk_warning): Same.  Adjust comments.
> 	* builtins.h (warn_string_no_nul): Add arguments.
> 	(struct access_ref): Add member and ctor argument.
> 	(struct access_data): Add members and ctor.
> 	(check_access): Adjust signature.
> 	* calls.c (maybe_warn_nonstring_arg): Return an indication of
> 	whether a warning was issued.  Issue -Wstrintop-overread instead
> 	of -Wstringop-overflow.
> 	(append_attrname): Adjust to naming changes.
> 	(maybe_warn_rdwr_sizes): Same.  Remove unnecessary and provide
> 	additional arguments in calls.
> 	* calls.h (maybe_warn_nonstring_arg): Return bool.
> 	* doc/invoke.texi (-Wstringop-overread): Document new option.
> 	* gimple-fold.c (gimple_fold_builtin_strcpy): Provide an additional
> 	argument in call.
> 	(gimple_fold_builtin_stpcpy): Same.
> 	* tree-ssa-uninit.c (maybe_warn_pass_by_reference): Adjust to naming
> 	changes.
> 	* tree.h (enum access_mode): New type.
> 
> gcc/c-family/ChangeLog:
> 
> 	* c.opt (Wstringop-overread): New option.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* c-c++-common/Warray-bounds-7.c: Adjust expected warnings.
> 	* c-c++-common/Wrestrict.c: Remove xfail.
> 	* c-c++-common/attr-nonstring-3.c: Adjust text of expected warnings.
> 	* c-c++-common/attr-nonstring-6.c: Suppress -Wstringop-overread
> 	instead of -Wstringop-overflow.
> 	* c-c++-common/attr-nonstring-8.c: Adjust text of expected warnings.
> 	* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Also suppress
> 	 -Wstringop-overread.
> 	* gcc.dg/Warray-bounds-39.c: Adjust expected warnings.
> 	* gcc.dg/Warray-bounds-40.c: Also suppress -Wstringop-overread.
> 	* gcc.dg/Warray-bounds-58.c: Remove xfail.  Also expect
> 	-Wstringop-overread.  Adjust text of expected warnings.
> 	* gcc.dg/Wsizeof-pointer-memaccess1.c: Also suppress
> 	 -Wstringop-overread.
> 	* gcc.dg/Wstringop-overflow-22.c: Adjust text of expected warnings.
> 	* gcc.dg/Wstringop-overflow-33.c: Expect -Wstringop-overread.
> 	* gcc.dg/Wstringop-overflow-9.c: Expect -Wstringop-overread.
> 	* gcc.dg/attr-nonstring-2.c: Adjust text of expected warnings.
> 	* gcc.dg/attr-nonstring-3.c: Same.
> 	* gcc.dg/attr-nonstring-4.c: Same.
> 	* gcc.dg/attr-nonstring.c: Expect -Wstringop-overread.
> 	* gcc.dg/builtin-stringop-chk-5.c: Adjust comment.
> 	* gcc.dg/builtin-stringop-chk-8.c: Enable -Wstringop-overread instead
> 	of -Wstringop-overflow.
> 	* gcc.dg/pr78902.c: Also expect -Wstringop-overread.
> 	* gcc.dg/pr79214.c: Adjust text of expected warnings.
> 	* gcc.dg/strcmpopt_10.c: Suppress valid -Wno-stringop-overread.
> 	* gcc.dg/strlenopt-57.c: Also expect -Wstringop-overread.
> 	* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Also suppress valid
> 	-Wno-stringop-overread.
> 	* gcc.dg/warn-strnlen-no-nul-2.c: Adjust text of expected warning.
> 	* gcc.dg/warn-strnlen-no-nul.c: Same.
> 	* gcc.dg/Wstringop-overread-2.c: New test.
> 	* gcc.dg/Wstringop-overread.c: New test.
I'll note you've got another mega patch here that should have been broken down
into smaller independent changes for easier/quicker review.

For example, there's a lot of churn because of the enum changes.  That's not a
functional change, it's just barely a step beyond a global search and replace and
could have been immediately approved and would have cut down on a lot of the
noise.

Similarly for the mechanical changes to add additional arguments to existing
functions.  The way to handle that is to add the arguments in the signature and
the call sites, but don't use them initially.  That makes no behavior change, is
trivial to ACK and again allows focus on the meat of the change.


Please please start thinking about how to break this kind of stuff out of the
main patch.  What looks obvious, simple and straightforward to the author is
often not for the reviewer.  The easier something is to review, generally the
faster it will get reviewed.

OK for the trunk,
jeff
Christophe Lyon Aug. 31, 2020, 10:51 a.m. UTC | #7
Hi,

On Wed, 26 Aug 2020 at 22:36, Jeff Law via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> On Tue, 2020-06-23 at 20:05 -0600, Martin Sebor wrote:
> > -Wstringop-overflow is issued for both writing and reading past
> > the end, even though the latter problem is often viewed as being
> > lower severity than the former, or at least sufficiently different
> > to triage separately.  In CWE, for example, each of the two kinds
> > of problems has its own classification (CWE-787: Out-of-bounds
> > Write, and CWE-125: Out-of-bounds Read).
> >
> > To make this easier with GCC, the attached patch introduces
> > a new option called -Wstringop-overread and splits the current
> > indiscriminate warning into the respective two categories.  Other
> > than the new option the improvements also exposed a few instances
> > of reading past the end that GCC doesn't detect that the new code
> > does thanks to more consistent checking.
> >
> > As usual, I tested the patch by building Binutils/GDB, Glibc, and
> > the Linux kernel with no unexpected (or any, in fact) instances
> > of the two warnings.
> >
> > The work involved more churn that I expected, and maintaining
> > the expected precedence of warnings (missing terminating nul,
> > excessive bounds, etc.) turned out to be more delicate and time
> > consuming than I would have liked.  I think the result is cleaner
> > code, but I'm quite sure it can still stand to be made better.
> > That's also my plan for the next step when I'd like to move this
> > checking out of builtins.c and calls.c and into a file of its own.
> > Ultimately, Jeff and have would like to integrate it into the path
> > isolation pass.
> >
> > Martin
> >
> > PS There's a FIXME comment in expand_builtin_memory_chk as
> > a reminder of a future change.  It currently has no bearing
> > on anything.
>
> > Add -Wstringop-overread for reading past the end by string functions.
> >
> > gcc/ChangeLog:
> >
> >       * attribs.c (init_attr_rdwr_indices): Use global access_mode.
> >       * attribs.h (struct attr_access): Same.
> >       * builtins.c (fold_builtin_strlen): Add argument.
> >       (compute_objsize): Declare.
> >       (get_range): Declare.
> >       (check_read_access): New function.
> >       (access_ref::access_ref): Define ctor.
> >       (warn_string_no_nul): Add arguments.  Handle -Wstrintop-overread.
> >       (check_nul_terminated_array): Handle source strings of different
> >       ranges of sizes.
> >       (expand_builtin_strlen): Remove warning code, call check_read_access
> >       instead.  Declare locals closer to their initialization.
> >       (expand_builtin_strnlen): Same.
> >       (maybe_warn_for_bound): New function.
> >       (warn_for_access): Remove argument.  Handle -Wstrintop-overread.
> >       (inform_access): Change argument type.
> >       (get_size_range): New function.
> >       (check_access): Remove unused arguments.  Add new arguments.  Handle
> >       -Wstrintop-overread.  Move warning code to helpers and call them.
> >       Call check_nul_terminated_array.
> >       (check_memop_access): Remove unnecessary and provide additional
> >       arguments in calls.
> >       (expand_builtin_memchr): Call check_read_access.
> >       (expand_builtin_strcat): Remove unnecessary and provide additional
> >       arguments in calls.
> >       (expand_builtin_strcpy): Same.
> >       (expand_builtin_strcpy_args): Same.  Avoid testing no-warning bit.
> >       (expand_builtin_stpcpy_1): Remove unnecessary and provide additional
> >       arguments in calls.
> >       (expand_builtin_stpncpy): Same.
> >       (check_strncat_sizes): Same.
> >       (expand_builtin_strncat): Remove unnecessary and provide additional
> >       arguments in calls.  Adjust comments.
> >       (expand_builtin_strncpy): Remove unnecessary and provide additional
> >       arguments in calls.
> >       (expand_builtin_memcmp): Remove warning code.  Call check_access.
> >       (expand_builtin_strcmp): Call check_access instead of
> >       check_nul_terminated_array.
> >       (expand_builtin_strncmp): Handle -Wstrintop-overread.
> >       (expand_builtin_fork_or_exec): Call check_access instead of
> >       check_nul_terminated_array.
> >       (expand_builtin): Same.
> >       (fold_builtin_1): Pass additional argument.
> >       (fold_builtin_n): Same.
> >       (fold_builtin_strpbrk): Remove calls to check_nul_terminated_array.
> >       (expand_builtin_memory_chk): Add comments.
> >       (maybe_emit_chk_warning): Remove unnecessary and provide additional
> >       arguments in calls.
> >       (maybe_emit_sprintf_chk_warning): Same.  Adjust comments.
> >       * builtins.h (warn_string_no_nul): Add arguments.
> >       (struct access_ref): Add member and ctor argument.
> >       (struct access_data): Add members and ctor.
> >       (check_access): Adjust signature.
> >       * calls.c (maybe_warn_nonstring_arg): Return an indication of
> >       whether a warning was issued.  Issue -Wstrintop-overread instead
> >       of -Wstringop-overflow.
> >       (append_attrname): Adjust to naming changes.
> >       (maybe_warn_rdwr_sizes): Same.  Remove unnecessary and provide
> >       additional arguments in calls.
> >       * calls.h (maybe_warn_nonstring_arg): Return bool.
> >       * doc/invoke.texi (-Wstringop-overread): Document new option.
> >       * gimple-fold.c (gimple_fold_builtin_strcpy): Provide an additional
> >       argument in call.
> >       (gimple_fold_builtin_stpcpy): Same.
> >       * tree-ssa-uninit.c (maybe_warn_pass_by_reference): Adjust to naming
> >       changes.
> >       * tree.h (enum access_mode): New type.
> >
> > gcc/c-family/ChangeLog:
> >
> >       * c.opt (Wstringop-overread): New option.
> >
> > gcc/testsuite/ChangeLog:
> >
> >       * c-c++-common/Warray-bounds-7.c: Adjust expected warnings.
> >       * c-c++-common/Wrestrict.c: Remove xfail.
> >       * c-c++-common/attr-nonstring-3.c: Adjust text of expected warnings.
> >       * c-c++-common/attr-nonstring-6.c: Suppress -Wstringop-overread
> >       instead of -Wstringop-overflow.
> >       * c-c++-common/attr-nonstring-8.c: Adjust text of expected warnings.
> >       * g++.dg/torture/Wsizeof-pointer-memaccess1.C: Also suppress
> >        -Wstringop-overread.
> >       * gcc.dg/Warray-bounds-39.c: Adjust expected warnings.
> >       * gcc.dg/Warray-bounds-40.c: Also suppress -Wstringop-overread.
> >       * gcc.dg/Warray-bounds-58.c: Remove xfail.  Also expect
> >       -Wstringop-overread.  Adjust text of expected warnings.
> >       * gcc.dg/Wsizeof-pointer-memaccess1.c: Also suppress
> >        -Wstringop-overread.
> >       * gcc.dg/Wstringop-overflow-22.c: Adjust text of expected warnings.
> >       * gcc.dg/Wstringop-overflow-33.c: Expect -Wstringop-overread.
> >       * gcc.dg/Wstringop-overflow-9.c: Expect -Wstringop-overread.
> >       * gcc.dg/attr-nonstring-2.c: Adjust text of expected warnings.
> >       * gcc.dg/attr-nonstring-3.c: Same.
> >       * gcc.dg/attr-nonstring-4.c: Same.
> >       * gcc.dg/attr-nonstring.c: Expect -Wstringop-overread.
> >       * gcc.dg/builtin-stringop-chk-5.c: Adjust comment.
> >       * gcc.dg/builtin-stringop-chk-8.c: Enable -Wstringop-overread instead
> >       of -Wstringop-overflow.
> >       * gcc.dg/pr78902.c: Also expect -Wstringop-overread.
> >       * gcc.dg/pr79214.c: Adjust text of expected warnings.
> >       * gcc.dg/strcmpopt_10.c: Suppress valid -Wno-stringop-overread.
> >       * gcc.dg/strlenopt-57.c: Also expect -Wstringop-overread.
> >       * gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Also suppress valid
> >       -Wno-stringop-overread.
> >       * gcc.dg/warn-strnlen-no-nul-2.c: Adjust text of expected warning.
> >       * gcc.dg/warn-strnlen-no-nul.c: Same.
> >       * gcc.dg/Wstringop-overread-2.c: New test.
> >       * gcc.dg/Wstringop-overread.c: New test.

I pushed a small aarch64 patch as obvious:
    2020-08-31  Christophe Lyon  <christophe.lyon@linaro.org>

        gcc/testsuite/
        * gcc.target/aarch64/strcmpopt_6.c: Suppress -Wstringop-overread.
(same as you added for i386)

On arm, there is a regression:
FAIL: c-c++-common/Warray-bounds-6.c  -Wc++-compat  (test for excess errors)
Excess errors:
/gcc/testsuite/c-c++-common/Warray-bounds-6.c:16:3: warning: 'strncpy'
writing 1 or more bytes into a region of size 0 overflows the
destination [-Wstringop-overflow=]

and the new test has several failures:
    gcc.dg/Wstringop-overread.c  (test for warnings, line 100)
    gcc.dg/Wstringop-overread.c  (test for warnings, line 110)
    gcc.dg/Wstringop-overread.c  (test for warnings, line 167)
    gcc.dg/Wstringop-overread.c  (test for warnings, line 177)
    gcc.dg/Wstringop-overread.c  (test for warnings, line 279)
    gcc.dg/Wstringop-overread.c  (test for warnings, line 289)
    gcc.dg/Wstringop-overread.c  (test for warnings, line 338)
    gcc.dg/Wstringop-overread.c  (test for warnings, line 372)
    gcc.dg/Wstringop-overread.c  (test for warnings, line 374)
    gcc.dg/Wstringop-overread.c  (test for warnings, line 532)
    gcc.dg/Wstringop-overread.c  (test for warnings, line 566)
    gcc.dg/Wstringop-overread.c  (test for warnings, line 568)
    gcc.dg/Wstringop-overread.c  (test for warnings, line 74)
    gcc.dg/Wstringop-overread.c  (test for warnings, line 84)
FAIL: gcc.dg/Wstringop-overread.c (test for excess errors)
Excess errors:
/gcc/testsuite/gcc.dg/Wstringop-overread.c:302:3: warning: 'strnlen'
specified bound [1, 4294967295] exceeds source size 0
[-Wstringop-overread]
/gcc/testsuite/gcc.dg/Wstringop-overread.c:306:3: warning: 'strnlen'
specified bound [1, 4294967295] exceeds source size 0
[-Wstringop-overread]
/gcc/testsuite/gcc.dg/Wstringop-overread.c:312:3: warning: 'strnlen'
specified bound [1, 4294967295] exceeds source size 0
[-Wstringop-overread]
/gcc/testsuite/gcc.dg/Wstringop-overread.c:323:3: warning: 'strnlen'
reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
/gcc/testsuite/gcc.dg/Wstringop-overread.c:351:3: warning: 'strnlen'
reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
/gcc/testsuite/gcc.dg/Wstringop-overread.c:498:3: warning: 'strndup'
specified bound [1, 4294967295] exceeds source size 0
[-Wstringop-overread]
/gcc/testsuite/gcc.dg/Wstringop-overread.c:502:3: warning: 'strndup'
specified bound [1, 4294967295] exceeds source size 0
[-Wstringop-overread]
/gcc/testsuite/gcc.dg/Wstringop-overread.c:508:3: warning: 'strndup'
specified bound [1, 4294967295] exceeds source size 0
[-Wstringop-overread]
/gcc/testsuite/gcc.dg/Wstringop-overread.c:518:3: warning: 'strndup'
reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
/gcc/testsuite/gcc.dg/Wstringop-overread.c:545:3: warning: 'strndup'
reading 1 or more bytes from a region of size 0 [-Wstringop-overread]

Can you check these?

Thanks,

Christophe



> I'll note you've got another mega patch here that should have been broken down
> into smaller independent changes for easier/quicker review.
>
> For example, there's a lot of churn because of the enum changes.  That's not a
> functional change, it's just barely a step beyond a global search and replace and
> could have been immediately approved and would have cut down on a lot of the
> noise.
>
> Similarly for the mechanical changes to add additional arguments to existing
> functions.  The way to handle that is to add the arguments in the signature and
> the call sites, but don't use them initially.  That makes no behavior change, is
> trivial to ACK and again allows focus on the meat of the change.
>
>
> Please please start thinking about how to break this kind of stuff out of the
> main patch.  What looks obvious, simple and straightforward to the author is
> often not for the reviewer.  The easier something is to review, generally the
> faster it will get reviewed.
>
> OK for the trunk,
> jeff
>
>
>
>
Martin Sebor Aug. 31, 2020, 9:50 p.m. UTC | #8
On 8/31/20 4:51 AM, Christophe Lyon wrote:
> Hi,
> 
...
> 
> I pushed a small aarch64 patch as obvious:
>      2020-08-31  Christophe Lyon  <christophe.lyon@linaro.org>
> 
>          gcc/testsuite/
>          * gcc.target/aarch64/strcmpopt_6.c: Suppress -Wstringop-overread.
> (same as you added for i386)

Thank you!

> 
> On arm, there is a regression:
> FAIL: c-c++-common/Warray-bounds-6.c  -Wc++-compat  (test for excess errors)
> Excess errors:
> /gcc/testsuite/c-c++-common/Warray-bounds-6.c:16:3: warning: 'strncpy'
> writing 1 or more bytes into a region of size 0 overflows the
> destination [-Wstringop-overflow=]
> 
> and the new test has several failures:
>      gcc.dg/Wstringop-overread.c  (test for warnings, line 100)
>      gcc.dg/Wstringop-overread.c  (test for warnings, line 110)
>      gcc.dg/Wstringop-overread.c  (test for warnings, line 167)
>      gcc.dg/Wstringop-overread.c  (test for warnings, line 177)
>      gcc.dg/Wstringop-overread.c  (test for warnings, line 279)
>      gcc.dg/Wstringop-overread.c  (test for warnings, line 289)
>      gcc.dg/Wstringop-overread.c  (test for warnings, line 338)
>      gcc.dg/Wstringop-overread.c  (test for warnings, line 372)
>      gcc.dg/Wstringop-overread.c  (test for warnings, line 374)
>      gcc.dg/Wstringop-overread.c  (test for warnings, line 532)
>      gcc.dg/Wstringop-overread.c  (test for warnings, line 566)
>      gcc.dg/Wstringop-overread.c  (test for warnings, line 568)
>      gcc.dg/Wstringop-overread.c  (test for warnings, line 74)
>      gcc.dg/Wstringop-overread.c  (test for warnings, line 84)
> FAIL: gcc.dg/Wstringop-overread.c (test for excess errors)
> Excess errors:
> /gcc/testsuite/gcc.dg/Wstringop-overread.c:302:3: warning: 'strnlen'
> specified bound [1, 4294967295] exceeds source size 0
> [-Wstringop-overread]
> /gcc/testsuite/gcc.dg/Wstringop-overread.c:306:3: warning: 'strnlen'
> specified bound [1, 4294967295] exceeds source size 0
> [-Wstringop-overread]
> /gcc/testsuite/gcc.dg/Wstringop-overread.c:312:3: warning: 'strnlen'
> specified bound [1, 4294967295] exceeds source size 0
> [-Wstringop-overread]
> /gcc/testsuite/gcc.dg/Wstringop-overread.c:323:3: warning: 'strnlen'
> reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
> /gcc/testsuite/gcc.dg/Wstringop-overread.c:351:3: warning: 'strnlen'
> reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
> /gcc/testsuite/gcc.dg/Wstringop-overread.c:498:3: warning: 'strndup'
> specified bound [1, 4294967295] exceeds source size 0
> [-Wstringop-overread]
> /gcc/testsuite/gcc.dg/Wstringop-overread.c:502:3: warning: 'strndup'
> specified bound [1, 4294967295] exceeds source size 0
> [-Wstringop-overread]
> /gcc/testsuite/gcc.dg/Wstringop-overread.c:508:3: warning: 'strndup'
> specified bound [1, 4294967295] exceeds source size 0
> [-Wstringop-overread]
> /gcc/testsuite/gcc.dg/Wstringop-overread.c:518:3: warning: 'strndup'
> reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
> /gcc/testsuite/gcc.dg/Wstringop-overread.c:545:3: warning: 'strndup'
> reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
> 
> Can you check these?

They should be fixed as of yesterday:
   https://gcc.gnu.org/pipermail/gcc-patches/2020-August/552976.html

Can you please retest with an updated build?

Martin
Martin Sebor Aug. 31, 2020, 10:26 p.m. UTC | #9
On 8/31/20 3:50 PM, Martin Sebor wrote:
> On 8/31/20 4:51 AM, Christophe Lyon wrote:
>> Hi,
>>
> ...
>>
>> I pushed a small aarch64 patch as obvious:
>>      2020-08-31  Christophe Lyon  <christophe.lyon@linaro.org>
>>
>>          gcc/testsuite/
>>          * gcc.target/aarch64/strcmpopt_6.c: Suppress 
>> -Wstringop-overread.
>> (same as you added for i386)
> 
> Thank you!
> 
>>
>> On arm, there is a regression:
>> FAIL: c-c++-common/Warray-bounds-6.c  -Wc++-compat  (test for excess 
>> errors)
>> Excess errors:
>> /gcc/testsuite/c-c++-common/Warray-bounds-6.c:16:3: warning: 'strncpy'
>> writing 1 or more bytes into a region of size 0 overflows the
>> destination [-Wstringop-overflow=]
>>
>> and the new test has several failures:
>>      gcc.dg/Wstringop-overread.c  (test for warnings, line 100)
>>      gcc.dg/Wstringop-overread.c  (test for warnings, line 110)
>>      gcc.dg/Wstringop-overread.c  (test for warnings, line 167)
>>      gcc.dg/Wstringop-overread.c  (test for warnings, line 177)
>>      gcc.dg/Wstringop-overread.c  (test for warnings, line 279)
>>      gcc.dg/Wstringop-overread.c  (test for warnings, line 289)
>>      gcc.dg/Wstringop-overread.c  (test for warnings, line 338)
>>      gcc.dg/Wstringop-overread.c  (test for warnings, line 372)
>>      gcc.dg/Wstringop-overread.c  (test for warnings, line 374)
>>      gcc.dg/Wstringop-overread.c  (test for warnings, line 532)
>>      gcc.dg/Wstringop-overread.c  (test for warnings, line 566)
>>      gcc.dg/Wstringop-overread.c  (test for warnings, line 568)
>>      gcc.dg/Wstringop-overread.c  (test for warnings, line 74)
>>      gcc.dg/Wstringop-overread.c  (test for warnings, line 84)
>> FAIL: gcc.dg/Wstringop-overread.c (test for excess errors)
>> Excess errors:
>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:302:3: warning: 'strnlen'
>> specified bound [1, 4294967295] exceeds source size 0
>> [-Wstringop-overread]
>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:306:3: warning: 'strnlen'
>> specified bound [1, 4294967295] exceeds source size 0
>> [-Wstringop-overread]
>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:312:3: warning: 'strnlen'
>> specified bound [1, 4294967295] exceeds source size 0
>> [-Wstringop-overread]
>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:323:3: warning: 'strnlen'
>> reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:351:3: warning: 'strnlen'
>> reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:498:3: warning: 'strndup'
>> specified bound [1, 4294967295] exceeds source size 0
>> [-Wstringop-overread]
>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:502:3: warning: 'strndup'
>> specified bound [1, 4294967295] exceeds source size 0
>> [-Wstringop-overread]
>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:508:3: warning: 'strndup'
>> specified bound [1, 4294967295] exceeds source size 0
>> [-Wstringop-overread]
>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:518:3: warning: 'strndup'
>> reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:545:3: warning: 'strndup'
>> reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
>>
>> Can you check these?
> 
> They should be fixed as of yesterday:
>    https://gcc.gnu.org/pipermail/gcc-patches/2020-August/552976.html

Actually, Wstringop-overread.c still fails a bunch of cases in LP32.
Let me see what's going on now.

Martin

> 
> Can you please retest with an updated build?
> 
> Martin
Christophe Lyon Sept. 1, 2020, 8:09 a.m. UTC | #10
On Mon, 31 Aug 2020 at 23:50, Martin Sebor <msebor@gmail.com> wrote:
>
> On 8/31/20 4:51 AM, Christophe Lyon wrote:
> > Hi,
> >
> ...
> >
> > I pushed a small aarch64 patch as obvious:
> >      2020-08-31  Christophe Lyon  <christophe.lyon@linaro.org>
> >
> >          gcc/testsuite/
> >          * gcc.target/aarch64/strcmpopt_6.c: Suppress -Wstringop-overread.
> > (same as you added for i386)
>
> Thank you!
>
> >
> > On arm, there is a regression:
> > FAIL: c-c++-common/Warray-bounds-6.c  -Wc++-compat  (test for excess errors)
> > Excess errors:
> > /gcc/testsuite/c-c++-common/Warray-bounds-6.c:16:3: warning: 'strncpy'
> > writing 1 or more bytes into a region of size 0 overflows the
> > destination [-Wstringop-overflow=]
> >
> > and the new test has several failures:
> >      gcc.dg/Wstringop-overread.c  (test for warnings, line 100)
> >      gcc.dg/Wstringop-overread.c  (test for warnings, line 110)
> >      gcc.dg/Wstringop-overread.c  (test for warnings, line 167)
> >      gcc.dg/Wstringop-overread.c  (test for warnings, line 177)
> >      gcc.dg/Wstringop-overread.c  (test for warnings, line 279)
> >      gcc.dg/Wstringop-overread.c  (test for warnings, line 289)
> >      gcc.dg/Wstringop-overread.c  (test for warnings, line 338)
> >      gcc.dg/Wstringop-overread.c  (test for warnings, line 372)
> >      gcc.dg/Wstringop-overread.c  (test for warnings, line 374)
> >      gcc.dg/Wstringop-overread.c  (test for warnings, line 532)
> >      gcc.dg/Wstringop-overread.c  (test for warnings, line 566)
> >      gcc.dg/Wstringop-overread.c  (test for warnings, line 568)
> >      gcc.dg/Wstringop-overread.c  (test for warnings, line 74)
> >      gcc.dg/Wstringop-overread.c  (test for warnings, line 84)
> > FAIL: gcc.dg/Wstringop-overread.c (test for excess errors)
> > Excess errors:
> > /gcc/testsuite/gcc.dg/Wstringop-overread.c:302:3: warning: 'strnlen'
> > specified bound [1, 4294967295] exceeds source size 0
> > [-Wstringop-overread]
> > /gcc/testsuite/gcc.dg/Wstringop-overread.c:306:3: warning: 'strnlen'
> > specified bound [1, 4294967295] exceeds source size 0
> > [-Wstringop-overread]
> > /gcc/testsuite/gcc.dg/Wstringop-overread.c:312:3: warning: 'strnlen'
> > specified bound [1, 4294967295] exceeds source size 0
> > [-Wstringop-overread]
> > /gcc/testsuite/gcc.dg/Wstringop-overread.c:323:3: warning: 'strnlen'
> > reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
> > /gcc/testsuite/gcc.dg/Wstringop-overread.c:351:3: warning: 'strnlen'
> > reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
> > /gcc/testsuite/gcc.dg/Wstringop-overread.c:498:3: warning: 'strndup'
> > specified bound [1, 4294967295] exceeds source size 0
> > [-Wstringop-overread]
> > /gcc/testsuite/gcc.dg/Wstringop-overread.c:502:3: warning: 'strndup'
> > specified bound [1, 4294967295] exceeds source size 0
> > [-Wstringop-overread]
> > /gcc/testsuite/gcc.dg/Wstringop-overread.c:508:3: warning: 'strndup'
> > specified bound [1, 4294967295] exceeds source size 0
> > [-Wstringop-overread]
> > /gcc/testsuite/gcc.dg/Wstringop-overread.c:518:3: warning: 'strndup'
> > reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
> > /gcc/testsuite/gcc.dg/Wstringop-overread.c:545:3: warning: 'strndup'
> > reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
> >
> > Can you check these?
>
> They should be fixed as of yesterday:
>    https://gcc.gnu.org/pipermail/gcc-patches/2020-August/552976.html
>
> Can you please retest with an updated build?

Indeed, thanks!

>
> Martin
Martin Sebor Sept. 1, 2020, 10:13 p.m. UTC | #11
On 9/1/20 2:09 AM, Christophe Lyon wrote:
> On Mon, 31 Aug 2020 at 23:50, Martin Sebor <msebor@gmail.com> wrote:
>>
>> On 8/31/20 4:51 AM, Christophe Lyon wrote:
>>> Hi,
>>>
>> ...
>>>
>>> I pushed a small aarch64 patch as obvious:
>>>       2020-08-31  Christophe Lyon  <christophe.lyon@linaro.org>
>>>
>>>           gcc/testsuite/
>>>           * gcc.target/aarch64/strcmpopt_6.c: Suppress -Wstringop-overread.
>>> (same as you added for i386)
>>
>> Thank you!
>>
>>>
>>> On arm, there is a regression:
>>> FAIL: c-c++-common/Warray-bounds-6.c  -Wc++-compat  (test for excess errors)
>>> Excess errors:
>>> /gcc/testsuite/c-c++-common/Warray-bounds-6.c:16:3: warning: 'strncpy'
>>> writing 1 or more bytes into a region of size 0 overflows the
>>> destination [-Wstringop-overflow=]
>>>
>>> and the new test has several failures:
>>>       gcc.dg/Wstringop-overread.c  (test for warnings, line 100)
>>>       gcc.dg/Wstringop-overread.c  (test for warnings, line 110)
>>>       gcc.dg/Wstringop-overread.c  (test for warnings, line 167)
>>>       gcc.dg/Wstringop-overread.c  (test for warnings, line 177)
>>>       gcc.dg/Wstringop-overread.c  (test for warnings, line 279)
>>>       gcc.dg/Wstringop-overread.c  (test for warnings, line 289)
>>>       gcc.dg/Wstringop-overread.c  (test for warnings, line 338)
>>>       gcc.dg/Wstringop-overread.c  (test for warnings, line 372)
>>>       gcc.dg/Wstringop-overread.c  (test for warnings, line 374)
>>>       gcc.dg/Wstringop-overread.c  (test for warnings, line 532)
>>>       gcc.dg/Wstringop-overread.c  (test for warnings, line 566)
>>>       gcc.dg/Wstringop-overread.c  (test for warnings, line 568)
>>>       gcc.dg/Wstringop-overread.c  (test for warnings, line 74)
>>>       gcc.dg/Wstringop-overread.c  (test for warnings, line 84)
>>> FAIL: gcc.dg/Wstringop-overread.c (test for excess errors)
>>> Excess errors:
>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:302:3: warning: 'strnlen'
>>> specified bound [1, 4294967295] exceeds source size 0
>>> [-Wstringop-overread]
>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:306:3: warning: 'strnlen'
>>> specified bound [1, 4294967295] exceeds source size 0
>>> [-Wstringop-overread]
>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:312:3: warning: 'strnlen'
>>> specified bound [1, 4294967295] exceeds source size 0
>>> [-Wstringop-overread]
>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:323:3: warning: 'strnlen'
>>> reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:351:3: warning: 'strnlen'
>>> reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:498:3: warning: 'strndup'
>>> specified bound [1, 4294967295] exceeds source size 0
>>> [-Wstringop-overread]
>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:502:3: warning: 'strndup'
>>> specified bound [1, 4294967295] exceeds source size 0
>>> [-Wstringop-overread]
>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:508:3: warning: 'strndup'
>>> specified bound [1, 4294967295] exceeds source size 0
>>> [-Wstringop-overread]
>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:518:3: warning: 'strndup'
>>> reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:545:3: warning: 'strndup'
>>> reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
>>>
>>> Can you check these?
>>
>> They should be fixed as of yesterday:
>>     https://gcc.gnu.org/pipermail/gcc-patches/2020-August/552976.html
>>
>> Can you please retest with an updated build?
> 
> Indeed, thanks!
> 

With this commit:
https://gcc.gnu.org/pipermail/gcc-patches/2020-September/553109.html
the remaining failures should now be gone.  Please let me know if
any persist.

Thanks
Martin
Li, Pan2 via Gcc-patches Sept. 1, 2020, 10:22 p.m. UTC | #12
On Tue, 2020-09-01 at 16:13 -0600, Martin Sebor wrote:
> On 9/1/20 2:09 AM, Christophe Lyon wrote:
> > On Mon, 31 Aug 2020 at 23:50, Martin Sebor <msebor@gmail.com> wrote:
> > > On 8/31/20 4:51 AM, Christophe Lyon wrote:
> > > > Hi,
> > > > 
> > > ...
> > > > I pushed a small aarch64 patch as obvious:
> > > >       2020-08-31  Christophe Lyon  <christophe.lyon@linaro.org>
> > > > 
> > > >           gcc/testsuite/
> > > >           * gcc.target/aarch64/strcmpopt_6.c: Suppress -Wstringop-overread.
> > > > (same as you added for i386)
> > > 
> > > Thank you!
> > > 
> > > > On arm, there is a regression:
> > > > FAIL: c-c++-common/Warray-bounds-6.c  -Wc++-compat  (test for excess errors)
> > > > Excess errors:
> > > > /gcc/testsuite/c-c++-common/Warray-bounds-6.c:16:3: warning: 'strncpy'
> > > > writing 1 or more bytes into a region of size 0 overflows the
> > > > destination [-Wstringop-overflow=]
> > > > 
> > > > and the new test has several failures:
> > > >       gcc.dg/Wstringop-overread.c  (test for warnings, line 100)
> > > >       gcc.dg/Wstringop-overread.c  (test for warnings, line 110)
> > > >       gcc.dg/Wstringop-overread.c  (test for warnings, line 167)
> > > >       gcc.dg/Wstringop-overread.c  (test for warnings, line 177)
> > > >       gcc.dg/Wstringop-overread.c  (test for warnings, line 279)
> > > >       gcc.dg/Wstringop-overread.c  (test for warnings, line 289)
> > > >       gcc.dg/Wstringop-overread.c  (test for warnings, line 338)
> > > >       gcc.dg/Wstringop-overread.c  (test for warnings, line 372)
> > > >       gcc.dg/Wstringop-overread.c  (test for warnings, line 374)
> > > >       gcc.dg/Wstringop-overread.c  (test for warnings, line 532)
> > > >       gcc.dg/Wstringop-overread.c  (test for warnings, line 566)
> > > >       gcc.dg/Wstringop-overread.c  (test for warnings, line 568)
> > > >       gcc.dg/Wstringop-overread.c  (test for warnings, line 74)
> > > >       gcc.dg/Wstringop-overread.c  (test for warnings, line 84)
> > > > FAIL: gcc.dg/Wstringop-overread.c (test for excess errors)
> > > > Excess errors:
> > > > /gcc/testsuite/gcc.dg/Wstringop-overread.c:302:3: warning: 'strnlen'
> > > > specified bound [1, 4294967295] exceeds source size 0
> > > > [-Wstringop-overread]
> > > > /gcc/testsuite/gcc.dg/Wstringop-overread.c:306:3: warning: 'strnlen'
> > > > specified bound [1, 4294967295] exceeds source size 0
> > > > [-Wstringop-overread]
> > > > /gcc/testsuite/gcc.dg/Wstringop-overread.c:312:3: warning: 'strnlen'
> > > > specified bound [1, 4294967295] exceeds source size 0
> > > > [-Wstringop-overread]
> > > > /gcc/testsuite/gcc.dg/Wstringop-overread.c:323:3: warning: 'strnlen'
> > > > reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
> > > > /gcc/testsuite/gcc.dg/Wstringop-overread.c:351:3: warning: 'strnlen'
> > > > reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
> > > > /gcc/testsuite/gcc.dg/Wstringop-overread.c:498:3: warning: 'strndup'
> > > > specified bound [1, 4294967295] exceeds source size 0
> > > > [-Wstringop-overread]
> > > > /gcc/testsuite/gcc.dg/Wstringop-overread.c:502:3: warning: 'strndup'
> > > > specified bound [1, 4294967295] exceeds source size 0
> > > > [-Wstringop-overread]
> > > > /gcc/testsuite/gcc.dg/Wstringop-overread.c:508:3: warning: 'strndup'
> > > > specified bound [1, 4294967295] exceeds source size 0
> > > > [-Wstringop-overread]
> > > > /gcc/testsuite/gcc.dg/Wstringop-overread.c:518:3: warning: 'strndup'
> > > > reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
> > > > /gcc/testsuite/gcc.dg/Wstringop-overread.c:545:3: warning: 'strndup'
> > > > reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
> > > > 
> > > > Can you check these?
> > > 
> > > They should be fixed as of yesterday:
> > >     https://gcc.gnu.org/pipermail/gcc-patches/2020-August/552976.html
> > > 
> > > Can you please retest with an updated build?
> > 
> > Indeed, thanks!
> > 
> 
> With this commit:
> https://gcc.gnu.org/pipermail/gcc-patches/2020-September/553109.html
> the remaining failures should now be gone.  Please let me know if
> any persist.
There's a related glibc build failure, but I think Joseph ack'd a fix for it
today.

jeff
>
Martin Sebor Sept. 2, 2020, midnight UTC | #13
On 9/1/20 4:22 PM, Jeff Law wrote:
> On Tue, 2020-09-01 at 16:13 -0600, Martin Sebor wrote:
>> On 9/1/20 2:09 AM, Christophe Lyon wrote:
>>> On Mon, 31 Aug 2020 at 23:50, Martin Sebor <msebor@gmail.com> wrote:
>>>> On 8/31/20 4:51 AM, Christophe Lyon wrote:
>>>>> Hi,
>>>>>
>>>> ...
>>>>> I pushed a small aarch64 patch as obvious:
>>>>>        2020-08-31  Christophe Lyon  <christophe.lyon@linaro.org>
>>>>>
>>>>>            gcc/testsuite/
>>>>>            * gcc.target/aarch64/strcmpopt_6.c: Suppress -Wstringop-overread.
>>>>> (same as you added for i386)
>>>>
>>>> Thank you!
>>>>
>>>>> On arm, there is a regression:
>>>>> FAIL: c-c++-common/Warray-bounds-6.c  -Wc++-compat  (test for excess errors)
>>>>> Excess errors:
>>>>> /gcc/testsuite/c-c++-common/Warray-bounds-6.c:16:3: warning: 'strncpy'
>>>>> writing 1 or more bytes into a region of size 0 overflows the
>>>>> destination [-Wstringop-overflow=]
>>>>>
>>>>> and the new test has several failures:
>>>>>        gcc.dg/Wstringop-overread.c  (test for warnings, line 100)
>>>>>        gcc.dg/Wstringop-overread.c  (test for warnings, line 110)
>>>>>        gcc.dg/Wstringop-overread.c  (test for warnings, line 167)
>>>>>        gcc.dg/Wstringop-overread.c  (test for warnings, line 177)
>>>>>        gcc.dg/Wstringop-overread.c  (test for warnings, line 279)
>>>>>        gcc.dg/Wstringop-overread.c  (test for warnings, line 289)
>>>>>        gcc.dg/Wstringop-overread.c  (test for warnings, line 338)
>>>>>        gcc.dg/Wstringop-overread.c  (test for warnings, line 372)
>>>>>        gcc.dg/Wstringop-overread.c  (test for warnings, line 374)
>>>>>        gcc.dg/Wstringop-overread.c  (test for warnings, line 532)
>>>>>        gcc.dg/Wstringop-overread.c  (test for warnings, line 566)
>>>>>        gcc.dg/Wstringop-overread.c  (test for warnings, line 568)
>>>>>        gcc.dg/Wstringop-overread.c  (test for warnings, line 74)
>>>>>        gcc.dg/Wstringop-overread.c  (test for warnings, line 84)
>>>>> FAIL: gcc.dg/Wstringop-overread.c (test for excess errors)
>>>>> Excess errors:
>>>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:302:3: warning: 'strnlen'
>>>>> specified bound [1, 4294967295] exceeds source size 0
>>>>> [-Wstringop-overread]
>>>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:306:3: warning: 'strnlen'
>>>>> specified bound [1, 4294967295] exceeds source size 0
>>>>> [-Wstringop-overread]
>>>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:312:3: warning: 'strnlen'
>>>>> specified bound [1, 4294967295] exceeds source size 0
>>>>> [-Wstringop-overread]
>>>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:323:3: warning: 'strnlen'
>>>>> reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
>>>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:351:3: warning: 'strnlen'
>>>>> reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
>>>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:498:3: warning: 'strndup'
>>>>> specified bound [1, 4294967295] exceeds source size 0
>>>>> [-Wstringop-overread]
>>>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:502:3: warning: 'strndup'
>>>>> specified bound [1, 4294967295] exceeds source size 0
>>>>> [-Wstringop-overread]
>>>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:508:3: warning: 'strndup'
>>>>> specified bound [1, 4294967295] exceeds source size 0
>>>>> [-Wstringop-overread]
>>>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:518:3: warning: 'strndup'
>>>>> reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
>>>>> /gcc/testsuite/gcc.dg/Wstringop-overread.c:545:3: warning: 'strndup'
>>>>> reading 1 or more bytes from a region of size 0 [-Wstringop-overread]
>>>>>
>>>>> Can you check these?
>>>>
>>>> They should be fixed as of yesterday:
>>>>      https://gcc.gnu.org/pipermail/gcc-patches/2020-August/552976.html
>>>>
>>>> Can you please retest with an updated build?
>>>
>>> Indeed, thanks!
>>>
>>
>> With this commit:
>> https://gcc.gnu.org/pipermail/gcc-patches/2020-September/553109.html
>> the remaining failures should now be gone.  Please let me know if
>> any persist.
> There's a related glibc build failure, but I think Joseph ack'd a fix for it
> today.

Yes, it was related to the warning change (though not to this fix).
I saw the Glibc patch go by yesterday.  The affected call is one
to memchr() from rawmemchr() where Glibc passes in SIZE_MAX as
the last argument.  It does it intentionally because  rawmemchr()
doesn't take a size argument and simply assumes the byte it looks
for exists in the array, so Glibc suppresses the (valid) warning.
With the change from -Wstringop-overflow to -overread the code
needed adjusting.

Martin
Joseph Myers Sept. 2, 2020, 5:46 p.m. UTC | #14
On Tue, 1 Sep 2020, Jeff Law via Gcc-patches wrote:

> > With this commit:
> > https://gcc.gnu.org/pipermail/gcc-patches/2020-September/553109.html
> > the remaining failures should now be gone.  Please let me know if
> > any persist.
> There's a related glibc build failure, but I think Joseph ack'd a fix for it
> today.

Note I'm not sure if Maciej will be committing that fix soon or not.

There is also at least one glibc testsuite build failure that appears on 
those architectures where the glibc build didn't fail.
diff mbox series

Patch

Add -Wstringop-overread for reading past the end by string functions.

gcc/ChangeLog:

	* attribs.c (init_attr_rdwr_indices): Use global access_mode.
	* attribs.h (struct attr_access): Same.
	* builtins.c (fold_builtin_strlen): Add argument.
	(compute_objsize): Declare.
	(get_range): Declare.
	(check_read_access): New function.
	(access_ref::access_ref): Define ctor.
	(warn_string_no_nul): Add arguments.  Handle -Wstrintop-overread.
	(check_nul_terminated_array): Handle source strings of different
	ranges of sizes.
	(expand_builtin_strlen): Remove warning code, call check_read_access
	instead.  Declare locals closer to their initialization.
	(expand_builtin_strnlen): Same.
	(maybe_warn_for_bound): New function.
	(warn_for_access): Remove argument.  Handle -Wstrintop-overread.
	(inform_access): Change argument type.
	(get_size_range): New function.
	(check_access): Remove unused arguments.  Add new arguments.  Handle
	-Wstrintop-overread.  Move warning code to helpers and call them.
	Call check_nul_terminated_array.
	(check_memop_access): Remove unnecessary and provide additional
	arguments in calls.
	(expand_builtin_memchr): Call check_read_access.
	(expand_builtin_strcat): Remove unnecessary and provide additional
	arguments in calls.
	(expand_builtin_strcpy): Same.
	(expand_builtin_strcpy_args): Same.  Avoid testing no-warning bit.
	(expand_builtin_stpcpy_1): Remove unnecessary and provide additional
	arguments in calls.
	(expand_builtin_stpncpy): Same.
	(check_strncat_sizes): Same.
	(expand_builtin_strncat): Remove unnecessary and provide additional
	arguments in calls.  Adjust comments.
	(expand_builtin_strncpy): Remove unnecessary and provide additional
	arguments in calls.
	(expand_builtin_memcmp): Remove warning code.  Call check_access.
	(expand_builtin_strcmp): Call check_access instead of
	check_nul_terminated_array.
	(expand_builtin_strncmp): Handle -Wstrintop-overread.
	(expand_builtin_fork_or_exec): Call check_access instead of
	check_nul_terminated_array.
	(expand_builtin): Same.
	(fold_builtin_1): Pass additional argument.
	(fold_builtin_n): Same.
	(fold_builtin_strpbrk): Remove calls to check_nul_terminated_array.
	(expand_builtin_memory_chk): Add comments.
	(maybe_emit_chk_warning): Remove unnecessary and provide additional
	arguments in calls.
	(maybe_emit_sprintf_chk_warning): Same.  Adjust comments.
	* builtins.h (warn_string_no_nul): Add arguments.
	(struct access_ref): Add member and ctor argument.
	(struct access_data): Add members and ctor.
	(check_access): Adjust signature.
	* calls.c (maybe_warn_nonstring_arg): Return an indication of
	whether a warning was issued.  Issue -Wstrintop-overread instead
	of -Wstringop-overflow.
	(append_attrname): Adjust to naming changes.
	(maybe_warn_rdwr_sizes): Same.  Remove unnecessary and provide
	additional arguments in calls.
	* calls.h (maybe_warn_nonstring_arg): Return bool.
	* doc/invoke.texi (-Wstringop-overread): Document new option.
	* gimple-fold.c (gimple_fold_builtin_strcpy): Provide an additional
	argument in call.
	(gimple_fold_builtin_stpcpy): Same.
	* tree-ssa-uninit.c (maybe_warn_pass_by_reference): Adjust to naming
	changes.
	* tree.h (enum access_mode): New type.

gcc/c-family/ChangeLog:

	* c.opt (Wstringop-overread): New option.

gcc/testsuite/ChangeLog:

	* c-c++-common/Warray-bounds-7.c: Adjust expected warnings.
	* c-c++-common/Wrestrict.c: Remove xfail.
	* c-c++-common/attr-nonstring-3.c: Adjust text of expected warnings.
	* c-c++-common/attr-nonstring-6.c: Suppress -Wstringop-overread
	instead of -Wstringop-overflow.
	* c-c++-common/attr-nonstring-8.c: Adjust text of expected warnings.
	* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Also suppress
	 -Wstringop-overread.
	* gcc.dg/Warray-bounds-39.c: Adjust expected warnings.
	* gcc.dg/Warray-bounds-40.c: Also suppress -Wstringop-overread.
	* gcc.dg/Warray-bounds-58.c: Remove xfail.  Also expect
	-Wstringop-overread.  Adjust text of expected warnings.
	* gcc.dg/Wsizeof-pointer-memaccess1.c: Also suppress
	 -Wstringop-overread.
	* gcc.dg/Wstringop-overflow-22.c: Adjust text of expected warnings.
	* gcc.dg/Wstringop-overflow-33.c: Expect -Wstringop-overread.
	* gcc.dg/Wstringop-overflow-9.c: Expect -Wstringop-overread.
	* gcc.dg/attr-nonstring-2.c: Adjust text of expected warnings.
	* gcc.dg/attr-nonstring-3.c: Same.
	* gcc.dg/attr-nonstring-4.c: Same.
	* gcc.dg/attr-nonstring.c: Expect -Wstringop-overread.
	* gcc.dg/builtin-stringop-chk-5.c: Adjust comment.
	* gcc.dg/builtin-stringop-chk-8.c: Enable -Wstringop-overread instead
	of -Wstringop-overflow.
	* gcc.dg/pr78902.c: Also expect -Wstringop-overread.
	* gcc.dg/pr79214.c: Adjust text of expected warnings.
	* gcc.dg/strcmpopt_10.c: Suppress valid -Wno-stringop-overread.
	* gcc.dg/strlenopt-57.c: Also expect -Wstringop-overread.
	* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Also suppress valid
	-Wno-stringop-overread.
	* gcc.dg/warn-strnlen-no-nul-2.c: Adjust text of expected warning.
	* gcc.dg/warn-strnlen-no-nul.c: Same.
	* gcc.dg/Wstringop-overread-2.c: New test.
	* gcc.dg/Wstringop-overread.c: New test.

diff --git a/gcc/attribs.c b/gcc/attribs.c
index 71dae123af8..7c0c18ff108 100644
--- a/gcc/attribs.c
+++ b/gcc/attribs.c
@@ -2045,10 +2045,10 @@  init_attr_rdwr_indices (rdwr_map *rwm, tree fntype)
 
 	  switch (*m)
 	    {
-	    case 'r': acc.mode = acc.read_only; break;
-	    case 'w': acc.mode = acc.write_only; break;
-	    case 'x': acc.mode = acc.read_write; break;
-	    case '-': acc.mode = acc.none; break;
+	    case 'r': acc.mode = access_read_only; break;
+	    case 'w': acc.mode = access_write_only; break;
+	    case 'x': acc.mode = access_read_write; break;
+	    case '-': acc.mode = access_none; break;
 	    default: gcc_unreachable ();
 	    }
 
diff --git a/gcc/attribs.h b/gcc/attribs.h
index dea0b6c44e6..6d0a9e48a8c 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -234,7 +234,6 @@  struct attr_access
   unsigned sizarg;
 
   /* The access mode.  */
-  enum access_mode { none, read_only, write_only, read_write };
   access_mode mode;
 };
 
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 4754602e0ec..c4d58e88931 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -153,7 +153,7 @@  static rtx expand_builtin_expect (tree, rtx);
 static rtx expand_builtin_expect_with_probability (tree, rtx);
 static tree fold_builtin_constant_p (tree);
 static tree fold_builtin_classify_type (tree);
-static tree fold_builtin_strlen (location_t, tree, tree);
+static tree fold_builtin_strlen (location_t, tree, tree, tree);
 static tree fold_builtin_inf (location_t, tree, int);
 static tree rewrite_call_expr (location_t, tree, int, tree, int, ...);
 static bool validate_arg (const_tree, enum tree_code code);
@@ -180,6 +180,9 @@  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 tree compute_objsize (tree, int, access_ref *, const vr_values * = NULL);
+static bool get_range (tree, signop, offset_int[2], const vr_values * = NULL);
+static bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
 
 unsigned HOST_WIDE_INT target_newline;
 unsigned HOST_WIDE_INT target_percent;
@@ -192,6 +195,26 @@  static tree do_mpfr_remquo (tree, tree, tree);
 static tree do_mpfr_lgamma_r (tree, tree, tree);
 static void expand_builtin_sync_synchronize (void);
 
+access_ref::access_ref (tree bound /* = NULL_TREE */,
+			bool minaccess /* = false */)
+  : ref ()
+{
+  /* Set to valid.  */
+  offrng[0] = offrng[1] = 0;
+  /* Invalidate.   */
+  sizrng[0] = sizrng[1] = -1;
+
+  /* Set the default bounds of the access and adjust below.  */
+  bndrng[0] = minaccess ? 1 : 0;
+  bndrng[1] = HOST_WIDE_INT_M1U;
+
+  /* When BOUND is nonnull and a range can be extracted from it,
+     set the bounds of the access to reflect both it and MINACCESS.
+     BNDRNG[0] is the size of the minimum access.  */
+  if (bound && get_range (bound, UNSIGNED, bndrng))
+    bndrng[0] = bndrng[0] > 0 && minaccess ? 1 : 0;
+}
+
 /* Return true if NAME starts with __builtin_ or __sync_.  */
 
 static bool
@@ -540,38 +563,132 @@  string_length (const void *ptr, unsigned eltsize, unsigned maxelts)
   return n;
 }
 
-/* For a call at LOC to a function FN that expects a string in the argument
-   ARG, issue a diagnostic due to it being a called with an argument
-   declared at NONSTR that is a character array with no terminating NUL.  */
+/* For a call EXPR at LOC to a function FNAME that expects a string
+   in the argument ARG, issue a diagnostic due to it being a called
+   with an argument that is a character array with no terminating
+   NUL.  SIZE is the EXACT size of the array, and BNDRNG the number
+   of characters in which the NUL is expected.  Either EXPR or FNAME
+   may be null but noth both.  SIZE may be null when BNDRNG is null.  */
 
 void
-warn_string_no_nul (location_t loc, const char *fn, tree arg, tree decl)
+warn_string_no_nul (location_t loc, tree expr, const char *fname,
+		    tree arg, tree decl, tree size /* = NULL_TREE */,
+		    bool exact /* = false */,
+		    const wide_int bndrng[2] /* = NULL */)
 {
-  if (TREE_NO_WARNING (arg))
+  if ((expr && TREE_NO_WARNING (expr)) || TREE_NO_WARNING (arg))
     return;
 
   loc = expansion_point_location_if_in_system_header (loc);
+  bool warned;
+
+  /* Format the bound range as a string to keep the nuber of messages
+     from exploding.  */
+  char bndstr[80];
+  *bndstr = 0;
+  if (bndrng)
+    {
+      if (bndrng[0] == bndrng[1])
+	sprintf (bndstr, "%llu", (unsigned long long) bndrng[0].to_uhwi ());
+      else
+	sprintf (bndstr, "[%llu, %llu]",
+		 (unsigned long long) bndrng[0].to_uhwi (),
+		 (unsigned long long) bndrng[1].to_uhwi ());
+    }
+
+  const tree maxobjsize = max_object_size ();
+  const wide_int maxsiz = wi::to_wide (maxobjsize);
+  if (expr)
+    {
+      tree func = get_callee_fndecl (expr);
+      if (bndrng)
+	{
+	  if (wi::ltu_p (maxsiz, bndrng[0]))
+	    warned = warning_at (loc, OPT_Wstringop_overread,
+				 "%K%qD specified bound %s exceeds "
+				 "maximum object size %E",
+				 expr, func, bndstr, maxobjsize);
+	  else
+	    {
+	      bool maybe = wi::to_wide (size) == bndrng[0];
+	      warned = warning_at (loc, OPT_Wstringop_overread,
+				   exact
+				   ? G_("%K%qD specified bound %s exceeds "
+					"the size %E of unterminated array")
+				   : (maybe
+				      ? G_("%K%qD specified bound %s may "
+					   "exceed the size of at most %E "
+					   "of unterminated array")
+				      : G_("%K%qD specified bound %s exceeds "
+					   "the size of at most %E "
+					   "of unterminated array")),
+				   expr, func, bndstr, size);
+	    }
+	}
+      else
+	warned = warning_at (loc, OPT_Wstringop_overread,
+			     "%K%qD argument missing terminating nul",
+			     expr, func);
+    }
+  else
+    {
+      if (bndrng)
+	{
+	  if (wi::ltu_p (maxsiz, bndrng[0]))
+	    warned = warning_at (loc, OPT_Wstringop_overread,
+				 "%qs specified bound %s exceeds "
+				 "maximum object size %E",
+				 fname, bndstr, maxobjsize);
+	  else
+	    {
+	      bool maybe = wi::to_wide (size) == bndrng[0];
+	      warned = warning_at (loc, OPT_Wstringop_overread,
+				   exact
+				   ? G_("%qs specified bound %s exceeds "
+					"the size %E of unterminated array")
+				   : (maybe
+				      ? G_("%qs specified bound %s may "
+					   "exceed the size of at most %E "
+					   "of unterminated array")
+				      : G_("%qs specified bound %s exceeds "
+					   "the size of at most %E "
+					   "of unterminated array")),
+				   fname, bndstr, size);
+	    }
+	}
+      else
+	warned = warning_at (loc, OPT_Wstringop_overread,
+			     "%qsargument missing terminating nul",
+			     fname);
+    }
 
-  if (warning_at (loc, OPT_Wstringop_overflow_,
-		  "%qs argument missing terminating nul", fn))
+  if (warned)
     {
       inform (DECL_SOURCE_LOCATION (decl),
 	      "referenced argument declared here");
       TREE_NO_WARNING (arg) = 1;
+      if (expr)
+	TREE_NO_WARNING (expr) = 1;
     }
 }
 
 /* For a call EXPR (which may be null) that expects a string argument
-   and SRC as the argument, returns false if SRC is a character array
-   with no terminating NUL.  When nonnull, BOUND is the number of
-   characters in which to expect the terminating NUL.
-   When EXPR is nonnull also issues a warning.  */
+   SRC as an argument, returns false if SRC is a character array with
+   no terminating NUL.  When nonnull, BOUND is the number of characters
+   in which to expect the terminating NUL.  RDONLY is true for read-only
+   accesses such as strcmp, false for read-write such as strcpy.  When
+   EXPR is also issues a warning.  */
 
 bool
-check_nul_terminated_array (tree expr, tree src, tree bound /* = NULL_TREE */)
+check_nul_terminated_array (tree expr, tree src,
+			    tree bound /* = NULL_TREE */)
 {
+  /* The constant size of the array SRC points to.  The actual size
+     may be less of EXACT is true, but not more.  */
   tree size;
+  /* True if SRC involves a non-constant offset into the array.  */
   bool exact;
+  /* The unterminated constant array SRC points to.  */
   tree nonstr = unterminated_array (src, &size, &exact);
   if (!nonstr)
     return true;
@@ -580,28 +697,30 @@  check_nul_terminated_array (tree expr, tree src, tree bound /* = NULL_TREE */)
      is the constant size of the array in bytes.  EXACT is true when
      SIZE is exact.  */
 
+  wide_int bndrng[2];
   if (bound)
     {
-      wide_int min, max;
       if (TREE_CODE (bound) == INTEGER_CST)
-	min = max = wi::to_wide (bound);
+	bndrng[0] = bndrng[1] = wi::to_wide (bound);
       else
 	{
-	  value_range_kind rng = get_range_info (bound, &min, &max);
+	  value_range_kind rng = get_range_info (bound, bndrng, bndrng + 1);
 	  if (rng != VR_RANGE)
 	    return true;
 	}
 
-      if (wi::leu_p (min, wi::to_wide (size)))
+      if (exact)
+	{
+	  if (wi::leu_p (bndrng[0], wi::to_wide (size)))
+	    return true;
+	}
+      else if (wi::lt_p (bndrng[0], wi::to_wide (size), UNSIGNED))
 	return true;
     }
 
-  if (expr && !TREE_NO_WARNING (expr))
-    {
-      tree fndecl = get_callee_fndecl (expr);
-      const char *fname = IDENTIFIER_POINTER (DECL_NAME (fndecl));
-      warn_string_no_nul (EXPR_LOCATION (expr), fname, src, nonstr);
-    }
+  if (expr)
+    warn_string_no_nul (EXPR_LOCATION (expr), expr, NULL, src, nonstr,
+			size, exact, bound ? bndrng : NULL);
 
   return false;
 }
@@ -2995,19 +3114,12 @@  expand_builtin_strlen (tree exp, rtx target,
   if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  class expand_operand ops[4];
-  rtx pat;
-  tree len;
   tree src = CALL_EXPR_ARG (exp, 0);
-  rtx src_reg;
-  rtx_insn *before_strlen;
-  machine_mode insn_mode;
-  enum insn_code icode = CODE_FOR_nothing;
-  unsigned int align;
+  if (!check_read_access (exp, src))
+    return NULL_RTX;
 
   /* If the length can be computed at compile-time, return it.  */
-  len = c_strlen (src, 0);
-  if (len)
+  if (tree len = c_strlen (src, 0))
     return expand_expr (len, target, target_mode, EXPAND_NORMAL);
 
   /* If the length can be computed at compile-time and is constant
@@ -3015,20 +3127,22 @@  expand_builtin_strlen (tree exp, rtx target,
      src for side-effects, then return len.
      E.g. x = strlen (i++ ? "xfoo" + 1 : "bar");
      can be optimized into: i++; x = 3;  */
-  len = c_strlen (src, 1);
+  tree len = c_strlen (src, 1);
   if (len && TREE_CODE (len) == INTEGER_CST)
     {
       expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
       return expand_expr (len, target, target_mode, EXPAND_NORMAL);
     }
 
-  align = get_pointer_alignment (src) / BITS_PER_UNIT;
+  unsigned int align = get_pointer_alignment (src) / BITS_PER_UNIT;
 
   /* If SRC is not a pointer type, don't do this operation inline.  */
   if (align == 0)
     return NULL_RTX;
 
   /* Bail out if we can't compute strlen in the right mode.  */
+  machine_mode insn_mode;
+  enum insn_code icode = CODE_FOR_nothing;
   FOR_EACH_MODE_FROM (insn_mode, target_mode)
     {
       icode = optab_handler (strlen_optab, insn_mode);
@@ -3041,12 +3155,13 @@  expand_builtin_strlen (tree exp, rtx target,
   /* Make a place to hold the source address.  We will not expand
      the actual source until we are sure that the expansion will
      not fail -- there are trees that cannot be expanded twice.  */
-  src_reg = gen_reg_rtx (Pmode);
+  rtx src_reg = gen_reg_rtx (Pmode);
 
   /* Mark the beginning of the strlen sequence so we can emit the
      source operand later.  */
-  before_strlen = get_last_insn ();
+  rtx_insn *before_strlen = get_last_insn ();
 
+  class expand_operand ops[4];
   create_output_operand (&ops[0], target, insn_mode);
   create_fixed_operand (&ops[1], gen_rtx_MEM (BLKmode, src_reg));
   create_integer_operand (&ops[2], 0);
@@ -3061,7 +3176,7 @@  expand_builtin_strlen (tree exp, rtx target,
 
   /* Now that we are assured of success, expand the source.  */
   start_sequence ();
-  pat = expand_expr (src, src_reg, Pmode, EXPAND_NORMAL);
+  rtx pat = expand_expr (src, src_reg, Pmode, EXPAND_NORMAL);
   if (pat != src_reg)
     {
 #ifdef POINTERS_EXTEND_UNSIGNED
@@ -3105,13 +3220,12 @@  expand_builtin_strnlen (tree exp, rtx target, machine_mode target_mode)
   if (!bound)
     return NULL_RTX;
 
+  check_read_access (exp, src, bound);
+
   location_t loc = UNKNOWN_LOCATION;
   if (EXPR_HAS_LOCATION (exp))
     loc = EXPR_LOCATION (exp);
 
-  tree maxobjsize = max_object_size ();
-  tree func = get_callee_fndecl (exp);
-
   /* FIXME: Change c_strlen() to return sizetype instead of ssizetype
      so these conversions aren't necessary.  */
   c_strlen_data lendata = { };
@@ -3121,48 +3235,6 @@  expand_builtin_strnlen (tree exp, rtx target, machine_mode target_mode)
 
   if (TREE_CODE (bound) == INTEGER_CST)
     {
-      if (!TREE_NO_WARNING (exp)
-	  && tree_int_cst_lt (maxobjsize, bound)
-	  && warning_at (loc, OPT_Wstringop_overflow_,
-			 "%K%qD specified bound %E "
-			 "exceeds maximum object size %E",
-			 exp, func, bound, maxobjsize))
-	TREE_NO_WARNING (exp) = true;
-
-      bool exact = true;
-      if (!len || TREE_CODE (len) != INTEGER_CST)
-	{
-	  /* Clear EXACT if LEN may be less than SRC suggests,
-	     such as in
-	       strnlen (&a[i], sizeof a)
-	     where the value of i is unknown.  Unless i's value is
-	     zero, the call is unsafe because the bound is greater. */
-	  lendata.decl = unterminated_array (src, &len, &exact);
-	  if (!lendata.decl)
-	    return NULL_RTX;
-	}
-
-      if (lendata.decl && (tree_int_cst_lt (len, bound) || !exact))
-	{
-	  location_t warnloc
-	    = expansion_point_location_if_in_system_header (loc);
-
-	  if (!TREE_NO_WARNING (exp)
-	      && warning_at (warnloc, OPT_Wstringop_overflow_,
-			     exact
-			     ? G_("%K%qD specified bound %E exceeds the size "
-				  "%E of unterminated array")
-			     : G_("%K%qD specified bound %E may exceed the "
-				  "size of at most %E of unterminated array"),
-			     exp, func, bound, len))
-	    {
-	      inform (DECL_SOURCE_LOCATION (lendata.decl),
-		      "referenced argument declared here");
-	      TREE_NO_WARNING (exp) = true;
-	    }
-	  return NULL_RTX;
-	}
-
       if (!len)
 	return NULL_RTX;
 
@@ -3178,44 +3250,14 @@  expand_builtin_strnlen (tree exp, rtx target, machine_mode target_mode)
   if (rng != VR_RANGE)
     return NULL_RTX;
 
-  if (!TREE_NO_WARNING (exp)
-      && wi::ltu_p (wi::to_wide (maxobjsize, min.get_precision ()), min)
-      && warning_at (loc, OPT_Wstringop_overflow_,
-		     "%K%qD specified bound [%wu, %wu] "
-		     "exceeds maximum object size %E",
-		     exp, func, min.to_uhwi (), max.to_uhwi (), maxobjsize))
-    TREE_NO_WARNING (exp) = true;
-
-  bool exact = true;
   if (!len || TREE_CODE (len) != INTEGER_CST)
     {
+      bool exact;
       lendata.decl = unterminated_array (src, &len, &exact);
       if (!lendata.decl)
 	return NULL_RTX;
     }
 
-  if (lendata.decl
-      && !TREE_NO_WARNING (exp)
-      && (wi::ltu_p (wi::to_wide (len), min)
-	  || !exact))
-    {
-      location_t warnloc
-	= expansion_point_location_if_in_system_header (loc);
-
-      if (warning_at (warnloc, OPT_Wstringop_overflow_,
-		      exact
-		      ? G_("%K%qD specified bound [%wu, %wu] exceeds "
-			   "the size %E of unterminated array")
-		      : G_("%K%qD specified bound [%wu, %wu] may exceed "
-			   "the size of at most %E of unterminated array"),
-		      exp, func, min.to_uhwi (), max.to_uhwi (), len))
-	{
-	  inform (DECL_SOURCE_LOCATION (lendata.decl),
-		  "referenced argument declared here");
-	  TREE_NO_WARNING (exp) = true;
-	}
-    }
-
   if (lendata.decl)
     return NULL_RTX;
 
@@ -3310,11 +3352,148 @@  determine_block_size (tree len, rtx len_rtx,
 			  GET_MODE_MASK (GET_MODE (len_rtx)));
 }
 
+/* Issue a warning OPT for a bounded call EXP with a bound in RANGE
+   accessing an object with SIZE.  */
+
+static bool
+maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
+		      tree bndrng[2], tree size, const access_data *pad = NULL)
+{
+  if (!bndrng[0] || TREE_NO_WARNING (exp))
+    return false;
+
+  tree maxobjsize = max_object_size ();
+
+  bool warned = false;
+
+  if (opt == OPT_Wstringop_overread)
+    {
+      if (tree_int_cst_lt (maxobjsize, bndrng[0]))
+	{
+	  if (bndrng[0] == bndrng[1])
+	    warned = (func
+		      ? warning_at (loc, opt,
+				    "%K%qD specified bound %E "
+				    "exceeds maximum object size %E",
+				    exp, func, bndrng[0], maxobjsize)
+		      : warning_at (loc, opt,
+				    "%Kspecified bound %E "
+				    "exceeds maximum object size %E",
+				    exp, bndrng[0], maxobjsize));
+	  else
+	    warned = (func
+		      ? warning_at (loc, opt,
+				    "%K%qD specified bound [%E, %E] "
+				    "exceeds maximum object size %E",
+				    exp, func,
+				    bndrng[0], bndrng[1], maxobjsize)
+		      : warning_at (loc, opt,
+				    "%Kspecified bound [%E, %E] "
+				    "exceeds maximum object size %E",
+				    exp, bndrng[0], bndrng[1], maxobjsize));
+	}
+      else if (!size || tree_int_cst_le (bndrng[0], size))
+	return false;
+      else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
+	warned = (func
+		  ? warning_at (loc, opt,
+				"%K%qD specified bound %E exceeds "
+				"source size %E",
+				exp, func, bndrng[0], size)
+		  : warning_at (loc, opt,
+				"%Kspecified bound %E exceeds "
+				"source size %E",
+				exp, bndrng[0], size));
+      else
+	warned = (func
+		  ? warning_at (loc, opt,
+				"%K%qD specified bound [%E, %E] exceeds "
+				"source size %E",
+				exp, func, bndrng[0], bndrng[1], size)
+		  : warning_at (loc, opt,
+				"%Kspecified bound [%E, %E] exceeds "
+				"source size %E",
+				exp, bndrng[0], bndrng[1], size));
+      if (warned)
+	{
+	  if (pad && pad->src.ref)
+	    {
+	      if (DECL_P (pad->src.ref))
+		inform (DECL_SOURCE_LOCATION (pad->src.ref),
+			"source object declared here");
+	      else if (EXPR_HAS_LOCATION (pad->src.ref))
+		inform (EXPR_LOCATION (pad->src.ref),
+			"source object allocated here");
+	    }
+	  TREE_NO_WARNING (exp) = true;
+	}
+
+      return warned;
+    }
+
+  if (tree_int_cst_lt (maxobjsize, bndrng[0]))
+    {
+      if (bndrng[0] == bndrng[1])
+	warned = (func
+		  ? warning_at (loc, opt,
+				"%K%qD specified size %E "
+				"exceeds maximum object size %E",
+				exp, func, bndrng[0], maxobjsize)
+		  : warning_at (loc, opt,
+				"%Kspecified size %E "
+				"exceeds maximum object size %E",
+				exp, bndrng[0], maxobjsize));
+      else
+	warned = (func
+		  ? warning_at (loc, opt,
+				"%K%qD specified size between %E and %E "
+				"exceeds maximum object size %E",
+				exp, func,
+				bndrng[0], bndrng[1], maxobjsize)
+		  : warning_at (loc, opt,
+				"%Kspecified size between %E and %E "
+				"exceeds maximum object size %E",
+				exp, bndrng[0], bndrng[1], maxobjsize));
+    }
+  else if (!size || tree_int_cst_le (bndrng[0], size))
+    return false;
+  else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
+    warned = (func
+	      ? warning_at (loc, OPT_Wstringop_overflow_,
+			    "%K%qD specified bound %E exceeds "
+			    "destination size %E",
+			    exp, func, bndrng[0], size)
+	      : warning_at (loc, OPT_Wstringop_overflow_,
+			    "%Kspecified bound %E exceeds "
+			    "destination size %E",
+			    exp, bndrng[0], size));
+  else
+    warned = (func
+	      ? warning_at (loc, OPT_Wstringop_overflow_,
+			    "%K%qD specified bound [%E, %E] exceeds "
+			    "destination size %E",
+			    exp, func, bndrng[0], bndrng[1], size)
+	      : warning_at (loc, OPT_Wstringop_overflow_,
+			    "%Kspecified bound [%E, %E] exceeds "
+			    "destination size %E",
+			    exp, bndrng[0], bndrng[1], size));
+
+  if (warned)
+    {
+      if (pad && pad->dst.ref)
+	inform (DECL_SOURCE_LOCATION (pad->dst.ref),
+		"destination object declared here");
+      TREE_NO_WARNING (exp) = true;
+    }
+
+  return warned;
+}
+
 /* For an expression EXP issue an access warning controlled by option OPT
    with access to a region SLEN bytes in size in the RANGE of sizes.  */
 
 static bool
-warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
+warn_for_access (location_t loc, tree func, tree exp, tree range[2],
 		 tree slen, bool access)
 {
   bool warned = false;
@@ -3323,11 +3502,13 @@  warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
     {
       if (tree_int_cst_equal (range[0], range[1]))
 	warned = (func
-		  ? warning_n (loc, opt, tree_to_uhwi (range[0]),
+		  ? warning_n (loc, OPT_Wstringop_overread,
+			       tree_to_uhwi (range[0]),
 			       "%K%qD reading %E byte from a region of size %E",
 			       "%K%qD reading %E bytes from a region of size %E",
 			       exp, func, range[0], slen)
-		  : warning_n (loc, opt, tree_to_uhwi (range[0]),
+		  : warning_n (loc, OPT_Wstringop_overread,
+			       tree_to_uhwi (range[0]),
 			       "%Kreading %E byte from a region of size %E",
 			       "%Kreading %E bytes from a region of size %E",
 			       exp, range[0], slen));
@@ -3335,36 +3516,41 @@  warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
 	{
 	  /* Avoid printing the upper bound if it's invalid.  */
 	  warned = (func
-		    ? warning_at (loc, opt,
+		    ? warning_at (loc, OPT_Wstringop_overread,
 				  "%K%qD reading %E or more bytes from a region "
 				  "of size %E",
 				  exp, func, range[0], slen)
-		    : warning_at (loc, opt,
+		    : warning_at (loc, OPT_Wstringop_overread,
 				  "%Kreading %E or more bytes from a region "
 				  "of size %E",
 				  exp, range[0], slen));
 	}
       else
 	warned = (func
-		  ? warning_at (loc, opt,
+		  ? warning_at (loc, OPT_Wstringop_overread,
 				"%K%qD reading between %E and %E bytes from "
 				"a region of size %E",
 				exp, func, range[0], range[1], slen)
-		  : warning_at (loc, opt,
+		  : warning_at (loc, OPT_Wstringop_overread,
 				"%Kreading between %E and %E bytes from "
 				"a region of size %E",
 				exp, range[0], range[1], slen));
 
+      if (warned)
+	TREE_NO_WARNING (exp) = true;
+
       return warned;
     }
 
   if (tree_int_cst_equal (range[0], range[1]))
     warned = (func
-	      ? warning_n (loc, opt, tree_to_uhwi (range[0]),
+	      ? warning_n (loc, OPT_Wstringop_overread,
+			   tree_to_uhwi (range[0]),
 			   "%K%qD epecting %E byte in a region of size %E",
 			   "%K%qD expecting %E bytes in a region of size %E",
 			   exp, func, range[0], slen)
-	      : warning_n (loc, opt, tree_to_uhwi (range[0]),
+	      : warning_n (loc, OPT_Wstringop_overread,
+			   tree_to_uhwi (range[0]),
 			   "%Kexpecting %E byte in a region of size %E",
 			   "%Kexpecting %E bytes in a region of size %E",
 			   exp, range[0], slen));
@@ -3372,25 +3558,29 @@  warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
     {
       /* Avoid printing the upper bound if it's invalid.  */
       warned = (func
-		? warning_at (loc, opt,
+		? warning_at (loc, OPT_Wstringop_overread,
 			      "%K%qD expecting %E or more bytes in a region "
 			      "of size %E",
 			      exp, func, range[0], slen)
-		: warning_at (loc, opt,
+		: warning_at (loc, OPT_Wstringop_overread,
 			      "%Kexpecting %E or more bytes in a region "
 			      "of size %E",
 			      exp, range[0], slen));
     }
   else
     warned = (func
-	      ? warning_at (loc, opt,
+	      ? warning_at (loc, OPT_Wstringop_overread,
 			    "%K%qD expecting between %E and %E bytes in "
 			    "a region of size %E",
 			    exp, func, range[0], range[1], slen)
-	      : warning_at (loc, opt,
+	      : warning_at (loc, OPT_Wstringop_overread,
 			    "%Kexpectting between %E and %E bytes in "
 			    "a region of size %E",
 			    exp, range[0], range[1], slen));
+
+  if (warned)
+    TREE_NO_WARNING (exp) = true;
+
   return warned;
 }
 
@@ -3398,7 +3588,7 @@  warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
    WRITE is set for a write access and clear for a read access.  */
 
 static void
-inform_access (const access_ref &ref, bool write)
+inform_access (const access_ref &ref, access_mode mode)
 {
   if (!ref.ref)
     return;
@@ -3448,7 +3638,7 @@  inform_access (const access_ref &ref, bool write)
   else
     loc = DECL_SOURCE_LOCATION (ref.ref);
 
-  if (write)
+  if (mode == access_read_write || mode == access_write_only)
     {
       if (DECL_P (ref.ref))
 	{
@@ -3518,6 +3708,34 @@  inform_access (const access_ref &ref, bool write)
 	    minoff, maxoff, sizestr, allocfn);
 }
 
+/* Helper to set RANGE to the range of BOUND if it's nonnull, bounded
+   by BNDRNG if nonnull and valid.  */
+
+static void
+get_size_range (tree bound, tree range[2], const offset_int bndrng[2])
+{
+  if (bound)
+    get_size_range (bound, range);
+
+  if (!bndrng || (bndrng[0] == 0 && bndrng[1] == HOST_WIDE_INT_M1U))
+    return;
+
+  if (range[0] && TREE_CODE (range[0]) == INTEGER_CST)
+    {
+      offset_int r[] =
+	{ wi::to_offset (range[0]), wi::to_offset (range[1]) };
+      if (r[0] < bndrng[0])
+	range[0] = wide_int_to_tree (sizetype, bndrng[0]);
+      if (bndrng[1] < r[1])
+	range[1] = wide_int_to_tree (sizetype, bndrng[1]);
+    }
+  else
+    {
+      range[0] = wide_int_to_tree (sizetype, bndrng[0]);
+      range[1] = wide_int_to_tree (sizetype, bndrng[1]);
+    }
+}
+
 /* Try to verify that the sizes and lengths of the arguments to a string
    manipulation function given by EXP are within valid bounds and that
    the operation does not lead to buffer overflow or read past the end.
@@ -3536,10 +3754,7 @@  inform_access (const access_ref &ref, bool write)
    like memcpy).  As an exception, SRCSTR can also be an integer denoting
    the precomputed size of the source string or object (for functions like
    memcpy).
-   DSTSIZE is the size of the destination object specified by the last
-   argument to the _chk builtins, typically resulting from the expansion
-   of __builtin_object_size (such as in __builtin___strcpy_chk(DST, SRC,
-   DSTSIZE).
+   DSTSIZE is the size of the destination object.
 
    When DSTWRITE is null LEN is checked to verify that it doesn't exceed
    SIZE_MAX.
@@ -3553,21 +3768,22 @@  inform_access (const access_ref &ref, bool write)
    return false.  */
 
 bool
-check_access (tree exp, tree, tree, tree dstwrite,
+check_access (tree exp, tree dstwrite,
 	      tree maxread, tree srcstr, tree dstsize,
-	      bool access /* = true */,
-	      const access_data *pad /* = NULL */)
+	      access_mode mode, const access_data *pad /* = NULL */)
 {
-  int opt = OPT_Wstringop_overflow_;
-
   /* The size of the largest object is half the address space, or
      PTRDIFF_MAX.  (This is way too permissive.)  */
   tree maxobjsize = max_object_size ();
 
-  /* Either the length of the source string for string functions or
-     the size of the source object for raw memory functions.  */
+  /* Either an approximate/minimum the length of the source string for
+     string functions or the size of the source object for raw memory
+     functions.  */
   tree slen = NULL_TREE;
 
+  /* The range of the access in bytes; first set to the write access
+     for functions that write and then read for those that also (or
+     just) read.  */
   tree range[2] = { NULL_TREE, NULL_TREE };
 
   /* Set to true when the exact number of bytes written by a string
@@ -3580,6 +3796,8 @@  check_access (tree exp, tree, tree, tree dstwrite,
 	 it can be an integer denoting the length of a string.  */
       if (POINTER_TYPE_P (TREE_TYPE (srcstr)))
 	{
+	  if (!check_nul_terminated_array (exp, srcstr, maxread))
+	    return false;
 	  /* Try to determine the range of lengths the source string
 	     refers to.  If it can be determined and is less than
 	     the upper bound given by MAXREAD add one to it for
@@ -3589,7 +3807,10 @@  check_access (tree exp, tree, tree, tree dstwrite,
 	  get_range_strlen (srcstr, &lendata, /* eltsize = */ 1);
 	  range[0] = lendata.minlen;
 	  range[1] = lendata.maxbound ? lendata.maxbound : lendata.maxlen;
-	  if (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
+	  if (range[0]
+	      && TREE_CODE (range[0]) == INTEGER_CST
+	      && TREE_CODE (range[1]) == INTEGER_CST
+	      && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
 	    {
 	      if (maxread && tree_int_cst_le (maxread, range[0]))
 		range[0] = range[1] = maxread;
@@ -3631,8 +3852,9 @@  check_access (tree exp, tree, tree, tree dstwrite,
   if (!dstsize)
     dstsize = maxobjsize;
 
-  if (dstwrite)
-    get_size_range (dstwrite, range);
+  /* Set RANGE to that of DSTWRITE if non-null, bounded by PAD->DST.BNDRNG
+     if valid.  */
+  get_size_range (dstwrite, range, pad ? pad->dst.bndrng : NULL);
 
   tree func = get_callee_fndecl (exp);
 
@@ -3642,37 +3864,11 @@  check_access (tree exp, tree, tree, tree dstwrite,
       && TREE_CODE (range[0]) == INTEGER_CST
       && tree_int_cst_lt (maxobjsize, range[0]))
     {
-      if (TREE_NO_WARNING (exp))
-	return false;
-
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
-      bool warned;
-      if (range[0] == range[1])
-	warned = (func
-		  ? warning_at (loc, opt,
-				"%K%qD specified size %E "
-				"exceeds maximum object size %E",
-				exp, func, range[0], maxobjsize)
-		  : warning_at (loc, opt,
-				"%Kspecified size %E "
-				"exceeds maximum object size %E",
-				exp, range[0], maxobjsize));
-      else
-	warned = (func
-		  ? warning_at (loc, opt,
-				"%K%qD specified size between %E and %E "
-				"exceeds maximum object size %E",
-				exp, func,
-				range[0], range[1], maxobjsize)
-		  : warning_at (loc, opt,
-				"%Kspecified size between %E and %E "
-				"exceeds maximum object size %E",
-				exp, range[0], range[1], maxobjsize));
-      if (warned)
-	TREE_NO_WARNING (exp) = true;
-
+      maybe_warn_for_bound (OPT_Wstringop_overflow_, loc, exp, func, range,
+			    NULL_TREE, pad);
       return false;
     }
 
@@ -3692,7 +3888,8 @@  check_access (tree exp, tree, tree, tree dstwrite,
 		  && tree_fits_uhwi_p (dstwrite)
 		  && tree_int_cst_lt (dstwrite, range[0]))))
 	{
-	  if (TREE_NO_WARNING (exp))
+	  if (TREE_NO_WARNING (exp)
+	      || (pad && pad->dst.ref && TREE_NO_WARNING (pad->dst.ref)))
 	    return false;
 
 	  location_t loc = tree_nonartificial_location (exp);
@@ -3705,12 +3902,12 @@  check_access (tree exp, tree, tree, tree dstwrite,
 		 and a source of unknown length.  The call will write
 		 at least one byte past the end of the destination.  */
 	      warned = (func
-			? warning_at (loc, opt,
+			? warning_at (loc, OPT_Wstringop_overflow_,
 				      "%K%qD writing %E or more bytes into "
 				      "a region of size %E overflows "
 				      "the destination",
 				      exp, func, range[0], dstsize)
-			: warning_at (loc, opt,
+			: warning_at (loc, OPT_Wstringop_overflow_,
 				      "%Kwriting %E or more bytes into "
 				      "a region of size %E overflows "
 				      "the destination",
@@ -3718,13 +3915,15 @@  check_access (tree exp, tree, tree, tree dstwrite,
 	    }
 	  else if (tree_int_cst_equal (range[0], range[1]))
 	    warned = (func
-		      ? warning_n (loc, opt, tree_to_uhwi (range[0]),
+		      ? warning_n (loc, OPT_Wstringop_overflow_,
+				   tree_to_uhwi (range[0]),
 				   "%K%qD writing %E byte into a region "
 				   "of size %E overflows the destination",
 				   "%K%qD writing %E bytes into a region "
 				   "of size %E overflows the destination",
 				   exp, func, range[0], dstsize)
-		      : warning_n (loc, opt, tree_to_uhwi (range[0]),
+		      : warning_n (loc, OPT_Wstringop_overflow_,
+				   tree_to_uhwi (range[0]),
 				   "%Kwriting %E byte into a region "
 				   "of size %E overflows the destination",
 				   "%Kwriting %E bytes into a region "
@@ -3734,12 +3933,12 @@  check_access (tree exp, tree, tree, tree dstwrite,
 	    {
 	      /* Avoid printing the upper bound if it's invalid.  */
 	      warned = (func
-			? warning_at (loc, opt,
+			? warning_at (loc, OPT_Wstringop_overflow_,
 				      "%K%qD writing %E or more bytes into "
 				      "a region of size %E overflows "
 				      "the destination",
 				      exp, func, range[0], dstsize)
-			: warning_at (loc, opt,
+			: warning_at (loc, OPT_Wstringop_overflow_,
 				      "%Kwriting %E or more bytes into "
 				      "a region of size %E overflows "
 				      "the destination",
@@ -3747,13 +3946,13 @@  check_access (tree exp, tree, tree, tree dstwrite,
 	    }
 	  else
 	    warned = (func
-		      ? warning_at (loc, opt,
+		      ? warning_at (loc, OPT_Wstringop_overflow_,
 				    "%K%qD writing between %E and %E bytes "
 				    "into a region of size %E overflows "
 				    "the destination",
 				    exp, func, range[0], range[1],
 				    dstsize)
-		      : warning_at (loc, opt,
+		      : warning_at (loc, OPT_Wstringop_overflow_,
 				    "%Kwriting between %E and %E bytes "
 				    "into a region of size %E overflows "
 				    "the destination",
@@ -3763,7 +3962,7 @@  check_access (tree exp, tree, tree, tree dstwrite,
 	    {
 	      TREE_NO_WARNING (exp) = true;
 	      if (pad)
-		inform_access (pad->dst, true);
+		inform_access (pad->dst, pad->mode);
 	    }
 
 	  /* Return error when an overflow has been detected.  */
@@ -3776,113 +3975,95 @@  check_access (tree exp, tree, tree, tree dstwrite,
      of an object.  */
   if (maxread)
     {
-      get_size_range (maxread, range);
-      if (range[0] && dstsize && tree_fits_uhwi_p (dstsize))
-	{
-	  location_t loc = tree_nonartificial_location (exp);
-	  loc = expansion_point_location_if_in_system_header (loc);
+      /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
+	 PAD is nonnull and BNDRNG is valid.  */
+      get_size_range (maxread, range, pad ? pad->src.bndrng : NULL);
+
+      location_t loc = tree_nonartificial_location (exp);
+      loc = expansion_point_location_if_in_system_header (loc);
 
+      tree size = dstsize;
+      if (pad && pad->mode == access_read_only)
+	size = wide_int_to_tree (sizetype, pad->src.sizrng[1]);
+
+      if (range[0] && maxread && tree_fits_uhwi_p (size))
+	{
 	  if (tree_int_cst_lt (maxobjsize, range[0]))
 	    {
-	      if (TREE_NO_WARNING (exp))
-		return false;
-
-	      bool warned = false;
-
-	      /* Warn about crazy big sizes first since that's more
-		 likely to be meaningful than saying that the bound
-		 is greater than the object size if both are big.  */
-	      if (range[0] == range[1])
-		warned = (func
-			  ? warning_at (loc, opt,
-					"%K%qD specified bound %E "
-					"exceeds maximum object size %E",
-					exp, func, range[0], maxobjsize)
-			  : warning_at (loc, opt,
-					"%Kspecified bound %E "
-					"exceeds maximum object size %E",
-					exp, range[0], maxobjsize));
-	      else
-		warned = (func
-			  ? warning_at (loc, opt,
-					"%K%qD specified bound between "
-					"%E and %E exceeds maximum object "
-					"size %E",
-					exp, func,
-					range[0], range[1], maxobjsize)
-			  : warning_at (loc, opt,
-					"%Kspecified bound between "
-					"%E and %E exceeds maximum object "
-					"size %E",
-					exp, range[0], range[1], maxobjsize));
-	      if (warned)
-		TREE_NO_WARNING (exp) = true;
-
+	      maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp, func,
+				    range, size, pad);
 	      return false;
 	    }
 
-	  if (dstsize != maxobjsize && tree_int_cst_lt (dstsize, range[0]))
+	  if (size != maxobjsize && tree_int_cst_lt (size, range[0]))
 	    {
-	      if (TREE_NO_WARNING (exp))
-		return false;
-
-	      bool warned = false;
-
-	      if (tree_int_cst_equal (range[0], range[1]))
-		warned = (func
-			  ? warning_at (loc, opt,
-					"%K%qD specified bound %E "
-					"exceeds destination size %E",
-					exp, func,
-					range[0], dstsize)
-			  : warning_at (loc, opt,
-					"%Kspecified bound %E "
-					"exceeds destination size %E",
-					exp, range[0], dstsize));
-	      else
-		warned = (func
-			  ? warning_at (loc, opt,
-					"%K%qD specified bound between %E "
-					"and %E exceeds destination size %E",
-					exp, func,
-					range[0], range[1], dstsize)
-			  : warning_at (loc, opt,
-					"%Kspecified bound between %E "
-					"and %E exceeds destination size %E",
-					exp,
-					range[0], range[1], dstsize));
-	      if (warned)
-		TREE_NO_WARNING (exp) = true;
-
+	      int opt = (dstwrite || mode != access_read_only
+			 ? OPT_Wstringop_overflow_
+			 : OPT_Wstringop_overread);
+	      maybe_warn_for_bound (opt, loc, exp, func, range, size, pad);
 	      return false;
 	    }
 	}
+
+      maybe_warn_nonstring_arg (func, exp);
     }
 
   /* Check for reading past the end of SRC.  */
-  if (slen
-      && slen == srcstr
-      && dstwrite && range[0]
-      && tree_int_cst_lt (slen, range[0]))
+  bool overread = (slen
+		   && slen == srcstr
+		   && 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 (TREE_NO_WARNING (exp))
+      /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
+	 PAD is nonnull and BNDRNG is valid.  */
+      get_size_range (maxread, range, pad ? pad->src.bndrng : NULL);
+      /* Set OVERREAD for reads starting just past the end of an object.  */
+      overread = pad->src.sizrng[1] - pad->src.offrng[0] < pad->src.bndrng[0];
+      range[0] = wide_int_to_tree (sizetype, pad->src.bndrng[0]);
+      slen = size_zero_node;
+    }
+
+  if (overread)
+    {
+      if (TREE_NO_WARNING (exp)
+	  || (srcstr && TREE_NO_WARNING (srcstr))
+	  || (pad && pad->src.ref && TREE_NO_WARNING (pad->src.ref)))
 	return false;
 
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
-      if (warn_for_access (loc, func, exp, opt, range, slen, access))
-	{
-	  TREE_NO_WARNING (exp) = true;
-	  if (pad)
-	    inform_access (pad->src, false);
-	}
+      if (warn_for_access (loc, func, exp, range, slen, mode)
+	  && pad)
+	inform_access (pad->src, access_read_only);
+
       return false;
     }
 
   return true;
 }
 
+/* A convenience wrapper for check_access above to check access
+   by a read-only function like puts.  */
+
+static bool
+check_read_access (tree exp, tree src, tree bound /* = NULL_TREE */,
+		   int ost /* = 1 */)
+{
+  if (!warn_stringop_overread)
+    return true;
+
+  access_data data (exp, access_read_only, NULL_TREE, false, bound, true);
+  compute_objsize (src, ost, &data.src);
+  return check_access (exp, /*dstwrite=*/ NULL_TREE, /*maxread=*/ bound,
+		       /*srcstr=*/ src, /*dstsize=*/ NULL_TREE, data.mode,
+		       &data);
+}
+
 /* If STMT is a call to an allocation function, returns the constant
    size of the object allocated by the call represented as sizetype.
    If nonnull, sets RNG1[] to the range of the size.  */
@@ -4221,7 +4402,7 @@  compute_objsize (tree ptr, int ostype, access_ref *pref,
 
 static tree
 compute_objsize (tree ptr, int ostype, access_ref *pref,
-		 const vr_values *rvals = NULL)
+		 const vr_values *rvals /* = NULL */)
 {
   bitmap visited = NULL;
 
@@ -4285,12 +4466,12 @@  check_memop_access (tree exp, tree dest, tree src, tree size)
      try to determine the size of the largest source and destination
      object using type-0 Object Size regardless of the object size
      type specified by the option.  */
-  access_data data;
+  access_data data (exp, access_read_write);
   tree srcsize = src ? compute_objsize (src, 0, &data.src) : NULL_TREE;
   tree dstsize = compute_objsize (dest, 0, &data.dst);
 
-  return check_access (exp, dest, src, size, /*maxread=*/NULL_TREE,
-		       srcsize, dstsize, true, &data);
+  return check_access (exp, size, /*maxread=*/NULL_TREE,
+		       srcsize, dstsize, data.mode, &data);
 }
 
 /* Validate memchr arguments without performing any expansion.
@@ -4306,16 +4487,7 @@  expand_builtin_memchr (tree exp, rtx)
   tree arg1 = CALL_EXPR_ARG (exp, 0);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  /* Diagnose calls where the specified length exceeds the size
-     of the object.  */
-  if (warn_stringop_overflow)
-    {
-      access_data data;
-      tree size = compute_objsize (arg1, 0, &data.src);
-      check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
-		    /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE,
-		    true, &data);
-    }
+  check_read_access (exp, arg1, len, 0);
 
   return NULL_RTX;
 }
@@ -4580,20 +4752,18 @@  expand_builtin_strcat (tree exp)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
 
-  /* Detect unterminated source (only).  */
-  if (!check_nul_terminated_array (exp, src))
-    return NULL_RTX;
-
   /* There is no way here to determine the length of the string in
      the destination to which the SRC string is being appended so
      just diagnose cases when the souce string is longer than
      the destination object.  */
+  access_data data (exp, access_read_write, NULL_TREE, true,
+		    NULL_TREE, true);
+  const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
+  compute_objsize (src, ost, &data.src);
+  tree destsize = compute_objsize (dest, ost, &data.dst);
 
-  access_data data;
-  tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
-
-  check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
-		destsize, true, &data);
+  check_access (exp, /*dstwrite=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		src, destsize, data.mode, &data);
 
   return NULL_RTX;
 }
@@ -4614,11 +4784,14 @@  expand_builtin_strcpy (tree exp, rtx target)
 
   if (warn_stringop_overflow)
     {
-      access_data data;
-      tree destsize = compute_objsize (dest, warn_stringop_overflow - 1,
-				       &data.dst);
-      check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
-		    src, destsize, true, &data);
+      access_data data (exp, access_read_write, NULL_TREE, true,
+			NULL_TREE, true);
+      const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
+      compute_objsize (src, ost, &data.src);
+      tree dstsize = compute_objsize (dest, ost, &data.dst);
+      check_access (exp, /*dstwrite=*/ NULL_TREE,
+		    /*maxread=*/ NULL_TREE, /*srcstr=*/ src,
+		    dstsize, data.mode, &data);
     }
 
   if (rtx ret = expand_builtin_strcpy_args (exp, dest, src, target))
@@ -4644,11 +4817,13 @@  static rtx
 expand_builtin_strcpy_args (tree exp, tree dest, tree src, rtx target)
 {
   /* Detect strcpy calls with unterminated arrays..  */
-  if (tree nonstr = unterminated_array (src))
+  tree size;
+  bool exact;
+  if (tree nonstr = unterminated_array (src, &size, &exact))
     {
       /* NONSTR refers to the non-nul terminated constant array.  */
-      if (!TREE_NO_WARNING (exp))
-	warn_string_no_nul (EXPR_LOCATION (exp), "strcpy", src, nonstr);
+      warn_string_no_nul (EXPR_LOCATION (exp), exp, NULL, src, nonstr,
+			  size, exact);
       return NULL_RTX;
     }
 
@@ -4674,11 +4849,11 @@  expand_builtin_stpcpy_1 (tree exp, rtx target, machine_mode mode)
 
   if (warn_stringop_overflow)
     {
-      access_data data;
+      access_data data (exp, access_read_write);
       tree destsize = compute_objsize (dst, warn_stringop_overflow - 1,
 				       &data.dst);
-      check_access (exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
-		    src, destsize, true, &data);
+      check_access (exp, /*dstwrite=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		    src, destsize, data.mode, &data);
     }
 
   /* If return value is ignored, transform stpcpy into strcpy.  */
@@ -4703,8 +4878,8 @@  expand_builtin_stpcpy_1 (tree exp, rtx target, machine_mode mode)
 	return expand_movstr (dst, src, target,
 			      /*retmode=*/ RETURN_END_MINUS_ONE);
 
-      if (lendata.decl && !TREE_NO_WARNING (exp))
-	warn_string_no_nul (EXPR_LOCATION (exp), "stpcpy", src, lendata.decl);
+      if (lendata.decl)
+	warn_string_no_nul (EXPR_LOCATION (exp), exp, NULL, src, lendata.decl);
 
       lenp1 = size_binop_loc (loc, PLUS_EXPR, len, ssize_int (1));
       ret = expand_builtin_mempcpy_args (dst, src, lenp1,
@@ -4784,15 +4959,10 @@  expand_builtin_stpncpy (tree exp, rtx)
 
   /* The exact number of bytes to write (not the maximum).  */
   tree len = CALL_EXPR_ARG (exp, 2);
-  if (!check_nul_terminated_array (exp, src, len))
-    return NULL_RTX;
-
-  access_data data;
+  access_data data (exp, access_read_write);
   /* The size of the destination object.  */
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
-
-  check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize,
-		true, &data);
+  check_access (exp, len, /*maxread=*/len, src, destsize, data.mode, &data);
 
   return NULL_RTX;
 }
@@ -4832,7 +5002,7 @@  check_strncat_sizes (tree exp, tree objsize)
   /* Try to verify that the destination is big enough for the shortest
      string.  */
 
-  access_data data;
+  access_data data (exp, access_read_write, maxread, true);
   if (!objsize && warn_stringop_overflow)
     {
       /* If it hasn't been provided by __strncat_chk, try to determine
@@ -4871,8 +5041,8 @@  check_strncat_sizes (tree exp, tree objsize)
 
   /* The number of bytes to write is LEN but check_access will alsoa
      check SRCLEN if LEN's value isn't known.  */
-  return check_access (exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
-		       objsize, true, &data);
+  return check_access (exp, /*dstwrite=*/NULL_TREE, maxread, srclen,
+		       objsize, data.mode, &data);
 }
 
 /* Similar to expand_builtin_strcat, do some very basic size validation
@@ -4910,7 +5080,7 @@  expand_builtin_strncat (tree exp, rtx)
       maxlen = lendata.maxbound;
     }
 
-  access_data data;
+  access_data data (exp, access_read_write);
   /* Try to verify that the destination is big enough for the shortest
      string.  First try to determine the size of the destination object
      into which the source is being copied.  */
@@ -4944,8 +5114,8 @@  expand_builtin_strncat (tree exp, rtx)
 	  && tree_int_cst_lt (maxread, srclen)))
     srclen = maxread;
 
-  check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize,
-		true, &data);
+  check_access (exp, /*dstwrite=*/NULL_TREE, maxread, srclen,
+		destsize, data.mode, &data);
 
   return NULL_RTX;
 }
@@ -4966,22 +5136,19 @@  expand_builtin_strncpy (tree exp, rtx target)
   /* The number of bytes to write (not the maximum).  */
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  if (!check_nul_terminated_array (exp, src, len))
-    return NULL_RTX;
-
   /* The length of the source sequence.  */
   tree slen = c_strlen (src, 1);
 
   if (warn_stringop_overflow)
     {
-      access_data data;
-      tree destsize = compute_objsize (dest, warn_stringop_overflow - 1,
-				       &data.dst);
-
+      access_data data (exp, access_read_write, len, true, len, true);
+      const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
+      compute_objsize (src, ost, &data.src);
+      tree dstsize = compute_objsize (dest, ost, &data.dst);
       /* The number of bytes to write is LEN but check_access will also
 	 check SLEN if LEN's value isn't known.  */
-      check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src,
-		    destsize, true, &data);
+      check_access (exp, /*dstwrite=*/len,
+		    /*maxread=*/len, src, dstsize, data.mode, &data);
     }
 
   /* We must be passed a constant len and src parameter.  */
@@ -5289,34 +5456,17 @@  expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
   tree arg1 = CALL_EXPR_ARG (exp, 0);
   tree arg2 = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
-  enum built_in_function fcode = DECL_FUNCTION_CODE (get_callee_fndecl (exp));
-  bool no_overflow = true;
 
   /* Diagnose calls where the specified length exceeds the size of either
      object.  */
-  access_data data;
-  tree size = compute_objsize (arg1, 0, &data.src);
-  no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE,
-			      len, /*maxread=*/NULL_TREE, size,
-			      /*objsize=*/NULL_TREE, true, &data);
-  if (no_overflow)
-    {
-      access_data data;
-      size = compute_objsize (arg2, 0, &data.src);
-      no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE,
-				  len,  /*maxread=*/NULL_TREE, size,
-				  /*objsize=*/NULL_TREE, true, &data);
-    }
-
-  /* If the specified length exceeds the size of either object, 
-     call the function.  */
-  if (!no_overflow)
+  if (!check_read_access (exp, arg1, len, 0)
+      || !check_read_access (exp, arg2, len, 0))
     return NULL_RTX;
 
   /* Due to the performance benefit, always inline the calls first
      when result_eq is false.  */
   rtx result = NULL_RTX;
-
+  enum built_in_function fcode = DECL_FUNCTION_CODE (get_callee_fndecl (exp));
   if (!result_eq && fcode != BUILT_IN_BCMP)
     {
       result = inline_expand_builtin_string_cmp (exp, target);
@@ -5399,8 +5549,8 @@  expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target)
   tree arg1 = CALL_EXPR_ARG (exp, 0);
   tree arg2 = CALL_EXPR_ARG (exp, 1);
 
-  if (!check_nul_terminated_array (exp, arg1)
-      || !check_nul_terminated_array (exp, arg2))
+  if (!check_read_access (exp, arg1)
+      || !check_read_access (exp, arg2))
     return NULL_RTX;
 
   /* Due to the performance benefit, always inline the calls first.  */
@@ -5508,8 +5658,8 @@  expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target)
 }
 
 /* Expand expression EXP, which is a call to the strncmp builtin. Return
-   NULL_RTX if we failed the caller should emit a normal call, otherwise try to get
-   the result in TARGET, if convenient.  */
+   NULL_RTX if we failed the caller should emit a normal call, otherwise
+   try to get the result in TARGET, if convenient.  */
 
 static rtx
 expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
@@ -5527,6 +5677,56 @@  expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
       || !check_nul_terminated_array (exp, arg2, arg3))
     return NULL_RTX;
 
+  location_t loc = tree_nonartificial_location (exp);
+  loc = expansion_point_location_if_in_system_header (loc);
+
+  tree len1 = c_strlen (arg1, 1);
+  tree len2 = c_strlen (arg2, 1);
+
+  if (!len1 || !len2)
+    {
+      /* Check to see if the argument was declared attribute nonstring
+	 and if so, issue a warning since at this point it's not known
+	 to be nul-terminated.  */
+      if (!maybe_warn_nonstring_arg (get_callee_fndecl (exp), exp)
+	  && !len1 && !len2)
+	{
+	  /* A strncmp read is constrained not just by the bound but
+	     also by the length of the shorter string.  Specifying
+	     a bound that's larger than the size of either array makes
+	     no sense and is likely a bug.  When the length of neither
+	     of the two strings is known but the sizes of both of
+	     the arrays they are stored in is, issue a warning if
+	     the bound is larger than than the size of the larger
+	     of the two arrays.  */
+
+	  access_ref ref1 (arg3, true);
+	  access_ref ref2 (arg3, true);
+
+	  tree bndrng[2] = { NULL_TREE, NULL_TREE };
+	  get_size_range (arg3, bndrng, ref1.bndrng);
+
+	  tree size1 = compute_objsize (arg1, 1, &ref1);
+	  tree size2 = compute_objsize (arg2, 1, &ref2);
+	  tree func = get_callee_fndecl (exp);
+
+	  if (size1 && size2)
+	    {
+	      tree maxsize = tree_int_cst_le (size1, size2) ? size2 : size1;
+
+	      if (tree_int_cst_lt (maxsize, bndrng[0]))
+		maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp, func,
+				      bndrng, maxsize);
+	    }
+	  else if (bndrng[0]
+		   && !integer_zerop (bndrng[0])
+		   && ((size1 && integer_zerop (size1))
+		       || (size2 && integer_zerop (size2))))
+	    maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp, func,
+				  bndrng, integer_zero_node);
+	}
+    }
+
   /* Due to the performance benefit, always inline the calls first.  */
   rtx result = NULL_RTX;
   result = inline_expand_builtin_string_cmp (exp, target);
@@ -5545,11 +5745,6 @@  expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
   unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT;
   unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT;
 
-  tree len1 = c_strlen (arg1, 1);
-  tree len2 = c_strlen (arg2, 1);
-
-  location_t loc = EXPR_LOCATION (exp);
-
   if (len1)
     len1 = size_binop_loc (loc, PLUS_EXPR, ssize_int (1), len1);
   if (len2)
@@ -5601,11 +5796,6 @@  expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
   tree fndecl = get_callee_fndecl (exp);
   if (result)
     {
-      /* Check to see if the argument was declared attribute nonstring
-	 and if so, issue a warning since at this point it's not known
-	 to be nul-terminated.  */
-      maybe_warn_nonstring_arg (fndecl, exp);
-
       /* Return the value in the proper mode for this function.  */
       mode = TYPE_MODE (TREE_TYPE (exp));
       if (GET_MODE (result) == mode)
@@ -5618,10 +5808,12 @@  expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
 
   /* Expand the library call ourselves using a stabilized argument
      list to avoid re-evaluating the function's arguments twice.  */
-  tree fn = build_call_nofold_loc (loc, fndecl, 3, arg1, arg2, len);
-  gcc_assert (TREE_CODE (fn) == CALL_EXPR);
-  CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
-  return expand_call (fn, target, target == const0_rtx);
+  tree call = build_call_nofold_loc (loc, fndecl, 3, arg1, arg2, len);
+  if (TREE_NO_WARNING (exp))
+    TREE_NO_WARNING (call) = true;
+  gcc_assert (TREE_CODE (call) == CALL_EXPR);
+  CALL_EXPR_TAILCALL (call) = CALL_EXPR_TAILCALL (exp);
+  return expand_call (call, target, target == const0_rtx);
 }
 
 /* Expand a call to __builtin_saveregs, generating the result in TARGET,
@@ -6538,8 +6730,9 @@  expand_builtin_fork_or_exec (tree fn, tree exp, rtx target, int ignore)
 
   if (DECL_FUNCTION_CODE (fn) != BUILT_IN_FORK)
     {
+      tree path = CALL_EXPR_ARG (exp, 0);
       /* Detect unterminated path.  */
-      if (!check_nul_terminated_array (exp, CALL_EXPR_ARG (exp, 0)))
+      if (!check_read_access (exp, path))
 	return NULL_RTX;
 
       /* Also detect unterminated first argument.  */
@@ -6548,7 +6741,7 @@  expand_builtin_fork_or_exec (tree fn, tree exp, rtx target, int ignore)
 	case BUILT_IN_EXECL:
 	case BUILT_IN_EXECLE:
 	case BUILT_IN_EXECLP:
-	  if (!check_nul_terminated_array (exp, CALL_EXPR_ARG (exp, 0)))
+	  if (!check_read_access (exp, path))
 	    return NULL_RTX;
 	default:
 	  break;
@@ -8217,7 +8410,7 @@  expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
     case BUILT_IN_PUTS_UNLOCKED:
     case BUILT_IN_STRDUP:
       if (validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
-	check_nul_terminated_array (exp, CALL_EXPR_ARG (exp, 0));
+	check_read_access (exp, CALL_EXPR_ARG (exp, 0));
       break;
 
     case BUILT_IN_INDEX:
@@ -8225,28 +8418,29 @@  expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
     case BUILT_IN_STRCHR:
     case BUILT_IN_STRRCHR:
       if (validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
-	check_nul_terminated_array (exp, CALL_EXPR_ARG (exp, 0));
+	check_read_access (exp, CALL_EXPR_ARG (exp, 0));
       break;
 
     case BUILT_IN_FPUTS:
     case BUILT_IN_FPUTS_UNLOCKED:
       if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
-	check_nul_terminated_array (exp, CALL_EXPR_ARG (exp, 0));
+	check_read_access (exp, CALL_EXPR_ARG (exp, 0));
       break;
 
     case BUILT_IN_STRNDUP:
       if (validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
-	check_nul_terminated_array (exp,
-				    CALL_EXPR_ARG (exp, 0),
-				    CALL_EXPR_ARG (exp, 1));
+	check_read_access (exp, CALL_EXPR_ARG (exp, 0), CALL_EXPR_ARG (exp, 1));
       break;
 
     case BUILT_IN_STRCASECMP:
+    case BUILT_IN_STRPBRK:
+    case BUILT_IN_STRSPN:
+    case BUILT_IN_STRCSPN:
     case BUILT_IN_STRSTR:
       if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
 	{
-	  check_nul_terminated_array (exp, CALL_EXPR_ARG (exp, 0));
-	  check_nul_terminated_array (exp, CALL_EXPR_ARG (exp, 1));
+	  check_read_access (exp, CALL_EXPR_ARG (exp, 0));
+	  check_read_access (exp, CALL_EXPR_ARG (exp, 1));
 	}
       break;
 
@@ -9276,10 +9470,11 @@  fold_builtin_classify_type (tree arg)
   return build_int_cst (integer_type_node, type_to_class (TREE_TYPE (arg)));
 }
 
-/* Fold a call to __builtin_strlen with argument ARG.  */
+/* Fold a call EXPR (which may be null) to __builtin_strlen with argument
+   ARG.  */
 
 static tree
-fold_builtin_strlen (location_t loc, tree type, tree arg)
+fold_builtin_strlen (location_t loc, tree expr, tree type, tree arg)
 {
   if (!validate_arg (arg, POINTER_TYPE))
     return NULL_TREE;
@@ -9300,7 +9495,7 @@  fold_builtin_strlen (location_t loc, tree type, tree arg)
 	    loc = EXPR_LOCATION (arg);
 	  else if (loc == UNKNOWN_LOCATION)
 	    loc = input_location;
-	  warn_string_no_nul (loc, "strlen", arg, lendata.decl);
+	  warn_string_no_nul (loc, expr, "strlen", arg, lendata.decl);
 	}
 
       return NULL_TREE;
@@ -10189,7 +10384,7 @@  fold_builtin_0 (location_t loc, tree fndecl)
    This function returns NULL_TREE if no simplification was possible.  */
 
 static tree
-fold_builtin_1 (location_t loc, tree fndecl, tree arg0)
+fold_builtin_1 (location_t loc, tree expr, tree fndecl, tree arg0)
 {
   tree type = TREE_TYPE (TREE_TYPE (fndecl));
   enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
@@ -10219,7 +10414,7 @@  fold_builtin_1 (location_t loc, tree fndecl, tree arg0)
       return fold_builtin_classify_type (arg0);
 
     case BUILT_IN_STRLEN:
-      return fold_builtin_strlen (loc, type, arg0);
+      return fold_builtin_strlen (loc, expr, type, arg0);
 
     CASE_FLT_FN (BUILT_IN_FABS):
     CASE_FLT_FN_FLOATN_NX (BUILT_IN_FABS):
@@ -10486,7 +10681,7 @@  fold_builtin_n (location_t loc, tree expr, tree fndecl, tree *args,
       ret = fold_builtin_0 (loc, fndecl);
       break;
     case 1:
-      ret = fold_builtin_1 (loc, fndecl, args[0]);
+      ret = fold_builtin_1 (loc, expr, fndecl, args[0]);
       break;
     case 2:
       ret = fold_builtin_2 (loc, expr, fndecl, args[0], args[1]);
@@ -10777,16 +10972,12 @@  readonly_data_expr (tree exp)
    form of the builtin function call.  */
 
 static tree
-fold_builtin_strpbrk (location_t loc, tree expr, tree s1, tree s2, tree type)
+fold_builtin_strpbrk (location_t loc, tree, tree s1, tree s2, tree type)
 {
   if (!validate_arg (s1, POINTER_TYPE)
       || !validate_arg (s2, POINTER_TYPE))
     return NULL_TREE;
 
-  if (!check_nul_terminated_array (expr, s1)
-      || !check_nul_terminated_array (expr, s2))
-    return NULL_TREE;
-
   tree fn;
   const char *p1, *p2;
 
@@ -11089,8 +11280,9 @@  expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
   tree len = CALL_EXPR_ARG (exp, 2);
   tree size = CALL_EXPR_ARG (exp, 3);
 
-  bool sizes_ok = check_access (exp, dest, src, len, /*maxread=*/NULL_TREE,
-				/*str=*/NULL_TREE, size);
+  /* FIXME: Set access mode to write only for memset et al.  */
+  bool sizes_ok = check_access (exp, len, /*maxread=*/NULL_TREE,
+				/*srcstr=*/NULL_TREE, size, access_read_write);
 
   if (!tree_fits_uhwi_p (size))
     return NULL_RTX;
@@ -11191,7 +11383,7 @@  maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
 {
   /* The source string.  */
   tree srcstr = NULL_TREE;
-  /* The size of the destination object.  */
+  /* The size of the destination object returned by __builtin_object_size.  */
   tree objsize = NULL_TREE;
   /* The string that is being concatenated with (as in __strcat_chk)
      or null if it isn't.  */
@@ -11202,6 +11394,9 @@  maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
   tree maxread = NULL_TREE;
   /* The exact size of the access (such as in __strncpy_chk).  */
   tree size = NULL_TREE;
+  /* The access by the function that's checked.  Except for snprintf
+     both writing and reading is checked.  */
+  access_mode mode = access_read_write;
 
   switch (fcode)
     {
@@ -11237,6 +11432,8 @@  maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
     case BUILT_IN_VSNPRINTF_CHK:
       maxread = CALL_EXPR_ARG (exp, 1);
       objsize = CALL_EXPR_ARG (exp, 3);
+      /* The only checked access the write to the destination.  */
+      mode = access_write_only;
       break;
     default:
       gcc_unreachable ();
@@ -11251,10 +11448,7 @@  maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
       return;
     }
 
-  /* The destination argument is the first one for all built-ins above.  */
-  tree dst = CALL_EXPR_ARG (exp, 0);
-
-  check_access (exp, dst, srcstr, size, maxread, srcstr, objsize);
+  check_access (exp, size, maxread, srcstr, objsize, mode);
 }
 
 /* Emit warning if a buffer overflow is detected at compile time
@@ -11311,8 +11505,8 @@  maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
   /* Add one for the terminating nul.  */
   len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
 
-  check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, /*size=*/NULL_TREE,
-		/*maxread=*/NULL_TREE, len, size);
+  check_access (exp, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, len, size,
+		access_write_only);
 }
 
 /* Emit warning if a free is called with address of a variable.  */
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 8b812ceb2c4..94ff96b1292 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -153,38 +153,56 @@  extern internal_fn associated_internal_fn (tree);
 extern internal_fn replacement_internal_fn (gcall *);
 
 extern bool check_nul_terminated_array (tree, tree, tree = NULL_TREE);
-extern void warn_string_no_nul (location_t, const char *, tree, tree);
+extern void warn_string_no_nul (location_t, tree, const char *, tree,
+				tree, tree = NULL_TREE, bool = false,
+				const wide_int[2] = NULL);
 extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
 extern bool builtin_with_linkage_p (tree);
 
 /* Describes a reference to an object used in an access.  */
 struct access_ref
 {
-  access_ref (): ref ()
-  {
-    /* Set to valid.  */
-    offrng[0] = offrng[1] = 0;
-    /* Invalidate.   */
-    sizrng[0] = sizrng[1] = -1;
-  }
-
-  /* Reference to the object.  */
+  /* Set the bounds of the reference to at most as many bytes
+     as the first argument or unknown when null, and at least
+     one when the second argument is true unless the first one
+     is a constant zero.  */
+  access_ref (tree = NULL_TREE, bool = false);
+
+  /* Reference to the accessed object(s).  */
   tree ref;
 
-  /* Range of offsets into and sizes of the object(s).  */
+  /* Range of byte offsets into and sizes of the object(s).  */
   offset_int offrng[2];
   offset_int sizrng[2];
+  /* Range of the bound of the access: denotes that the access
+     is at least BNDRNG[0] bytes but no more than BNDRNG[1].
+     For string functions the size of the actual access is
+     further constrained by the length of the string.  */
+  offset_int bndrng[2];
 };
 
 /* Describes a pair of references used in an access by built-in
    functions like memcpy.  */
 struct access_data
 {
+  /* Set the access to at most MAXWRITE and MAXREAD bytes, and
+     at least 1 when MINWRITE or MINREAD, respectively, is set.  */
+  access_data (tree expr, access_mode mode,
+	       tree maxwrite = NULL_TREE, bool minwrite = false,
+	       tree maxread = NULL_TREE, bool minread = false)
+    : call (expr),
+      dst (maxwrite, minwrite), src (maxread, minread), mode (mode) { }
+
+  /* Built-in function call.  */
+  tree call;
   /* Destination and source of the access.  */
   access_ref dst, src;
+  /* Read-only for functions like memcmp or strlen, write-only
+     for memset, read-write for memcpy or strcat.  */
+  access_mode mode;
 };
 
-extern bool check_access (tree, tree, tree, tree, tree, tree, tree,
-			  bool = true, const access_data * = NULL);
+extern bool check_access (tree, tree, tree, tree, tree,
+			  access_mode, const access_data * = NULL);
 
 #endif /* GCC_BUILTINS_H */
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 89a58282b3f..5e38feaf500 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -823,6 +823,10 @@  C ObjC C++ LTO ObjC++ Joined RejectNegative UInteger Var(warn_stringop_overflow)
 Under the control of Object Size type, warn about buffer overflow in string
 manipulation functions like memcpy and strcpy.
 
+Wstringop-overread
+C ObjC C++ LTO ObjC++ Var(warn_stringop_overread) Init(1) Warning LangEnabledBy(C ObjC C++ LTO ObjC++, Wall)
+Warn about reading past the end of a source array in string manipulation functions like memchr and memcpy.
+
 Wstringop-truncation
 C ObjC C++ LTO ObjC++ Var(warn_stringop_truncation) Warning Init (1) LangEnabledBy(C ObjC C++ LTO ObjC++, Wall)
 Warn about truncation in string manipulation functions like strncat and strncpy.
diff --git a/gcc/calls.c b/gcc/calls.c
index d1c9c0b159a..2b2688b611e 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1559,22 +1559,23 @@  get_attr_nonstring_decl (tree expr, tree *ref)
   return NULL_TREE;
 }
 
-/* Warn about passing a non-string array/pointer to a function that
-   expects a nul-terminated string argument.  */
+/* Warn about passing a non-string array/pointer to a built-in function
+   that expects a nul-terminated string argument.  Returns true if
+   a warning has been issued.*/
 
-void
+bool
 maybe_warn_nonstring_arg (tree fndecl, tree exp)
 {
   if (!fndecl || !fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
-    return;
+    return false;
 
-  if (TREE_NO_WARNING (exp) || !warn_stringop_overflow)
-    return;
+  if (TREE_NO_WARNING (exp) || !warn_stringop_overread)
+    return false;
 
   /* Avoid clearly invalid calls (more checking done below).  */
   unsigned nargs = call_expr_nargs (exp);
   if (!nargs)
-    return;
+    return false;
 
   /* The bound argument to a bounded string function like strncpy.  */
   tree bound = NULL_TREE;
@@ -1666,22 +1667,27 @@  maybe_warn_nonstring_arg (tree fndecl, tree exp)
 
   if (bndrng[0])
     {
-      /* Diagnose excessive bound prior the adjustment below and
+      /* Diagnose excessive bound prior to the adjustment below and
 	 regardless of attribute nonstring.  */
       tree maxobjsize = max_object_size ();
       if (tree_int_cst_lt (maxobjsize, bndrng[0]))
 	{
+	  bool warned = false;
 	  if (tree_int_cst_equal (bndrng[0], bndrng[1]))
-	    warning_at (loc, OPT_Wstringop_overflow_,
-			"%K%qD specified bound %E "
-			"exceeds maximum object size %E",
-			exp, fndecl, bndrng[0], maxobjsize);
+	    warned = warning_at (loc, OPT_Wstringop_overread,
+				 "%K%qD specified bound %E "
+				 "exceeds maximum object size %E",
+				 exp, fndecl, bndrng[0], maxobjsize);
 	  else
-	    warning_at (loc, OPT_Wstringop_overflow_,
-			"%K%qD specified bound [%E, %E] "
-			"exceeds maximum object size %E",
-			exp, fndecl, bndrng[0], bndrng[1], maxobjsize);
-	  return;
+	    warned = warning_at (loc, OPT_Wstringop_overread,
+				 "%K%qD specified bound [%E, %E] "
+				 "exceeds maximum object size %E",
+				 exp, fndecl, bndrng[0], bndrng[1],
+				 maxobjsize);
+	  if (warned)
+	    TREE_NO_WARNING (exp) = true;
+
+	  return warned;
 	}
     }
 
@@ -1710,6 +1716,7 @@  maybe_warn_nonstring_arg (tree fndecl, tree exp)
 	}
     }
 
+  bool any_arg_warned = false;
   /* Iterate over the built-in function's formal arguments and check
      each const char* against the actual argument.  If the actual
      argument is declared attribute non-string issue a warning unless
@@ -1820,19 +1827,19 @@  maybe_warn_nonstring_arg (tree fndecl, tree exp)
       if (wi::ltu_p (asize, wibnd))
 	{
 	  if (bndrng[0] == bndrng[1])
-	    warned = warning_at (loc, OPT_Wstringop_overflow_,
+	    warned = warning_at (loc, OPT_Wstringop_overread,
 				 "%qD argument %i declared attribute "
 				 "%<nonstring%> is smaller than the specified "
 				 "bound %wu",
 				 fndecl, argno + 1, wibnd.to_uhwi ());
 	  else if (wi::ltu_p (asize, wi::to_offset (bndrng[0])))
-	    warned = warning_at (loc, OPT_Wstringop_overflow_,
+	    warned = warning_at (loc, OPT_Wstringop_overread,
 				 "%qD argument %i declared attribute "
 				 "%<nonstring%> is smaller than "
 				 "the specified bound [%E, %E]",
 				 fndecl, argno + 1, bndrng[0], bndrng[1]);
 	  else
-	    warned = warning_at (loc, OPT_Wstringop_overflow_,
+	    warned = warning_at (loc, OPT_Wstringop_overread,
 				 "%qD argument %i declared attribute "
 				 "%<nonstring%> may be smaller than "
 				 "the specified bound [%E, %E]",
@@ -1842,14 +1849,22 @@  maybe_warn_nonstring_arg (tree fndecl, tree exp)
 	; /* Avoid warning for calls to strncat() when the bound
 	     is equal to the size of the non-string argument.  */
       else if (!bound)
-	warned = warning_at (loc, OPT_Wstringop_overflow_,
+	warned = warning_at (loc, OPT_Wstringop_overread,
 			     "%qD argument %i declared attribute %<nonstring%>",
 			     fndecl, argno + 1);
 
       if (warned)
-	inform (DECL_SOURCE_LOCATION (decl),
-		"argument %qD declared here", decl);
+	{
+	  inform (DECL_SOURCE_LOCATION (decl),
+		  "argument %qD declared here", decl);
+	  any_arg_warned = true;
+	}
     }
+
+  if (any_arg_warned)
+    TREE_NO_WARNING (exp) = true;
+
+  return any_arg_warned;
 }
 
 /* Issue an error if CALL_EXPR was flagged as requiring
@@ -1896,11 +1911,11 @@  append_attrname (const std::pair<int, attr_access> &access,
   size_t len = strlen (attrstr);
 
   const char* const atname
-    = (access.second.mode == attr_access::read_only
+    = (access.second.mode == access_read_only
        ? "read_only"
-       : (access.second.mode == attr_access::write_only
+       : (access.second.mode == access_write_only
 	  ? "write_only"
-	  : (access.second.mode == attr_access::read_write
+	  : (access.second.mode == access_read_write
 	     ? "read_write" : "none")));
 
   const char *sep = len ? ", " : "";
@@ -2059,7 +2074,7 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp)
       tree objsize = compute_objsize (ptr, 0);
 
       tree srcsize;
-      if (access.second.mode == attr_access::write_only)
+      if (access.second.mode == access_write_only)
 	{
 	  /* For a write-only argument there is no source.  */
 	  srcsize = NULL_TREE;
@@ -2069,8 +2084,8 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp)
 	  /* For read-only and read-write attributes also set the source
 	     size.  */
 	  srcsize = objsize;
-	  if (access.second.mode == attr_access::read_only
-	      || access.second.mode == attr_access::none)
+	  if (access.second.mode == access_read_only
+	      || access.second.mode == access_none)
 	    {
 	      /* For a read-only attribute there is no destination so
 		 clear OBJSIZE.  This emits "reading N bytes" kind of
@@ -2084,8 +2099,8 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp)
 	 iteration so that accesses via different arguments are
 	 diagnosed.  */
       TREE_NO_WARNING (exp) = false;
-      check_access (exp, NULL_TREE, NULL_TREE, size, /*maxread=*/ NULL_TREE,
-		    srcsize, objsize, access.second.mode != attr_access::none);
+      check_access (exp, size, /*maxread=*/ NULL_TREE, srcsize, objsize,
+		    access.second.mode);
 
       if (TREE_NO_WARNING (exp))
 	/* If check_access issued a warning above, append the relevant
diff --git a/gcc/calls.h b/gcc/calls.h
index 4ee49360777..dfb951ca45b 100644
--- a/gcc/calls.h
+++ b/gcc/calls.h
@@ -132,7 +132,7 @@  extern bool reference_callee_copied (CUMULATIVE_ARGS *,
 				     const function_arg_info &);
 extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]);
 extern tree get_attr_nonstring_decl (tree, tree * = NULL);
-extern void maybe_warn_nonstring_arg (tree, tree);
+extern bool maybe_warn_nonstring_arg (tree, tree);
 extern bool get_size_range (tree, tree[2], bool = false);
 extern rtx rtx_for_static_chain (const_tree, bool);
 extern bool cxx17_empty_base_field_p (const_tree);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 74ab149a035..c69ba5bbdbb 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -363,7 +363,8 @@  Objective-C and Objective-C++ Dialects}.
 -Wstack-protector  -Wstack-usage=@var{byte-size}  -Wstrict-aliasing @gol
 -Wstrict-aliasing=n  -Wstrict-overflow  -Wstrict-overflow=@var{n} @gol
 -Wstring-compare @gol
--Wstringop-overflow=@var{n}  -Wno-stringop-truncation @gol
+-Wno-stringop-overflow -Wno-stringop-overread @gol
+-Wno-stringop-truncation @gol
 -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}malloc@r{]} @gol
 -Wswitch  -Wno-switch-bool  -Wswitch-default  -Wswitch-enum @gol
 -Wno-switch-outside-range  -Wno-switch-unreachable  -Wsync-nand @gol
@@ -6678,6 +6679,7 @@  void f (char *d)
 
 @option{-Wstring-compare} is enabled by @option{-Wextra}.
 
+@item -Wno-stringop-overflow
 @item -Wstringop-overflow
 @itemx -Wstringop-overflow=@var{type}
 @opindex Wstringop-overflow
@@ -6719,6 +6721,15 @@  const char* f (enum Color clr)
 
 Option @option{-Wstringop-overflow=2} is enabled by default.
 
+@item -Wno-stringop-overread
+@opindex Wstringop-overread
+@opindex Wno-stringop-overread
+Warn for calls to string manipulation functions such as @code{memchr},
+@code{strcpy} that are determined to read past the end of the source
+sequence. 
+
+Option @option{-Wstringop-overread} is enabled by default.
+
 @table @gcctabopt
 @item -Wstringop-overflow
 @itemx -Wstringop-overflow=1
@@ -7426,8 +7437,12 @@  This warning is also enabled by @option{-Wshadow=local}.
 Warn whenever an object is defined whose size exceeds @var{byte-size}.
 @option{-Wlarger-than=}@samp{PTRDIFF_MAX} is enabled by default.
 Warnings controlled by the option can be disabled either by specifying
-@var{byte-size} of @samp{SIZE_MAX} or more or by
-@option{-Wno-larger-than}.
+@var{byte-size} of @samp{SIZE_MAX} or more or by @option{-Wno-larger-than}.
+
+Also warn for calls to bounded functions such as @code{memchr} or
+@code{strnlen} that specify a bound greater than the largest possible
+object, which is @samp{PTRDIFF_MAX} bytes by default.  These warnings
+can only be disabled by @option{-Wno-larger-than}.
 
 @item -Wno-larger-than
 @opindex Wno-larger-than
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 4e3de95d2d2..2bd111902dc 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -1837,7 +1837,7 @@  gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
     {
       /* Avoid folding calls with unterminated arrays.  */
       if (!gimple_no_warning_p (stmt))
-	warn_string_no_nul (loc, "strcpy", src, nonstr);
+	warn_string_no_nul (loc, NULL_TREE, "strcpy", src, nonstr);
       gimple_set_no_warning (stmt, true);
       return false;
     }
@@ -3036,11 +3036,16 @@  gimple_fold_builtin_stpcpy (gimple_stmt_iterator *gsi)
 
   /* Set to non-null if ARG refers to an unterminated array.  */
   c_strlen_data data = { };
+  /* The size of the unterminated array if SRC referes to one.  */
+  tree size;
+  /* True if the size is exact/constant, false if it's the lower bound
+     of a range.  */
+  bool exact;
   tree len = c_strlen (src, 1, &data, 1);
   if (!len
       || TREE_CODE (len) != INTEGER_CST)
     {
-      data.decl = unterminated_array (src);
+      data.decl = unterminated_array (src, &size, &exact);
       if (!data.decl)
 	return false;
     }
@@ -3049,7 +3054,8 @@  gimple_fold_builtin_stpcpy (gimple_stmt_iterator *gsi)
     {
       /* Avoid folding calls with unterminated arrays.  */
       if (!gimple_no_warning_p (stmt))
-	warn_string_no_nul (loc, "stpcpy", src, data.decl);
+	warn_string_no_nul (loc, NULL_TREE, "stpcpy", src, data.decl, size,
+			    exact);
       gimple_set_no_warning (stmt, true);
       return false;
     }
diff --git a/gcc/testsuite/c-c++-common/Warray-bounds-7.c b/gcc/testsuite/c-c++-common/Warray-bounds-7.c
index 668e8096200..131645406dd 100644
--- a/gcc/testsuite/c-c++-common/Warray-bounds-7.c
+++ b/gcc/testsuite/c-c++-common/Warray-bounds-7.c
@@ -29,52 +29,52 @@  void sink (unsigned);
 
 void test_zero_length_array (void)
 {
-  T (a0.a);                   // { dg-warning "\\\[-Warray-bounds" }
-  T (a0.a - 1);               // { dg-warning "\\\[-Warray-bounds" }
-  T (a0.a + 1);               // { dg-warning "\\\[-Warray-bounds" }
-  T (a0.a + 9);               // { dg-warning "\\\[-Warray-bounds" }
-  T (a0.a + INT_MAX);         // { dg-warning "\\\[-Warray-bounds" }
-  T (a0.a + PTRDIFF_MAX);     // { dg-warning "\\\[-Warray-bounds" }
+  T (a0.a);                   // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a0.a - 1);               // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a0.a + 1);               // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a0.a + 9);               // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a0.a + INT_MAX);         // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a0.a + PTRDIFF_MAX);     // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
   T (a0.a + SIZE_MAX);        // { dg-warning "\\\[-Warray-bounds" }
 
   T (a0_0.a);                 // { dg-warning "\\\[-Warray-bounds" }
   T (a0_0.a - 1);             // { dg-warning "\\\[-Warray-bounds" }
-  T (a0_0.a + 1);             // { dg-warning "\\\[-Warray-bounds" }
-  T (a0_0.a + 9);             // { dg-warning "\\\[-Warray-bounds" }
-  T (a0_0.a + INT_MAX);       // { dg-warning "\\\[-Warray-bounds" }
-  T (a0_0.a + PTRDIFF_MAX);   // { dg-warning "\\\[-Warray-bounds" }
+  T (a0_0.a + 1);             // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a0_0.a + 9);             // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a0_0.a + INT_MAX);       // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a0_0.a + PTRDIFF_MAX);   // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
   T (a0_0.a + SIZE_MAX);      // { dg-warning "\\\[-Warray-bounds" }
 
   T (a0_0_.a);                // { dg-warning "\\\[-Warray-bounds" }
   T (a0_0_.a - 1);            // { dg-warning "\\\[-Warray-bounds" }
-  T (a0_0_.a + 1);            // { dg-warning "\\\[-Warray-bounds" }
-  T (a0_0_.a + 9);            // { dg-warning "\\\[-Warray-bounds" }
-  T (a0_0_.a + INT_MAX);      // { dg-warning "\\\[-Warray-bounds" }
-  T (a0_0_.a + PTRDIFF_MAX);  // { dg-warning "\\\[-Warray-bounds" }
+  T (a0_0_.a + 1);            // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a0_0_.a + 9);            // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a0_0_.a + INT_MAX);      // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a0_0_.a + PTRDIFF_MAX);  // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
   T (a0_0_.a + SIZE_MAX);     // { dg-warning "\\\[-Warray-bounds" }
 }
 
 void test_one_element_array (void)
 {
   T (a1.a - 1);               // { dg-warning "\\\[-Warray-bounds" }
-  T (a1.a + 1);               // { dg-warning "\\\[-Warray-bounds" }
-  T (a1.a + 9);               // { dg-warning "\\\[-Warray-bounds" }
-  T (a1.a + INT_MAX);         // { dg-warning "\\\[-Warray-bounds" }
-  T (a1.a + PTRDIFF_MAX);     // { dg-warning "\\\[-Warray-bounds" }
+  T (a1.a + 1);               // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a1.a + 9);               // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a1.a + INT_MAX);         // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a1.a + PTRDIFF_MAX);     // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
   T (a1.a + SIZE_MAX);        // { dg-warning "\\\[-Warray-bounds" }
 
   T (a1_0.a - 1);             // { dg-warning "\\\[-Warray-bounds" }
-  T (a1_0.a + 1);             // { dg-warning "\\\[-Warray-bounds" }
-  T (a1_0.a + 9);             // { dg-warning "\\\[-Warray-bounds" }
-  T (a1_0.a + INT_MAX);       // { dg-warning "\\\[-Warray-bounds" }
-  T (a1_0.a + PTRDIFF_MAX);   // { dg-warning "\\\[-Warray-bounds" }
+  T (a1_0.a + 1);             // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a1_0.a + 9);             // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a1_0.a + INT_MAX);       // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a1_0.a + PTRDIFF_MAX);   // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
   T (a1_0.a + SIZE_MAX);      // { dg-warning "\\\[-Warray-bounds" }
 
   T (a1_0_.a - 1);            // { dg-warning "\\\[-Warray-bounds" }
-  T (a1_0_.a + 1);            // { dg-warning "\\\[-Warray-bounds" }
-  T (a1_0_.a + 9);            // { dg-warning "\\\[-Warray-bounds" }
-  T (a1_0_.a + INT_MAX);      // { dg-warning "\\\[-Warray-bounds" }
-  T (a1_0_.a + PTRDIFF_MAX);  // { dg-warning "\\\[-Warray-bounds" }
+  T (a1_0_.a + 1);            // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a1_0_.a + 9);            // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a1_0_.a + INT_MAX);      // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (a1_0_.a + PTRDIFF_MAX);  // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
   T (a1_0_.a + SIZE_MAX);     // { dg-warning "\\\[-Warray-bounds" }
 }
 
@@ -82,26 +82,25 @@  void test_flexible_array_member (void)
 {
   T (ax.a);                   // { dg-warning "\\\[-Warray-bounds" }
   T (ax.a - 1);               // { dg-warning "\\\[-Warray-bounds" }
-  T (ax.a + 1);               // { dg-warning "\\\[-Warray-bounds" }
-  T (ax.a + 9);               // { dg-warning "\\\[-Warray-bounds" }
-  T (ax.a + INT_MAX);         // { dg-warning "\\\[-Warray-bounds" }
-  T (ax.a + PTRDIFF_MAX);     // { dg-warning "\\\[-Warray-bounds" }
+  T (ax.a + 1);               // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (ax.a + 9);               // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (ax.a + INT_MAX);         // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (ax.a + PTRDIFF_MAX);     // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
   T (ax.a + SIZE_MAX);        // { dg-warning "\\\[-Warray-bounds" }
 
   T (ax_0.a);                 // { dg-warning "\\\[-Warray-bounds" }
   T (ax_0.a - 1);             // { dg-warning "\\\[-Warray-bounds" }
-  T (ax_0.a + 1);             // { dg-warning "\\\[-Warray-bounds" }
-  T (ax_0.a + 9);             // { dg-warning "\\\[-Warray-bounds" }
-  T (ax_0.a + INT_MAX);       // { dg-warning "\\\[-Warray-bounds" }
-  T (ax_0.a + PTRDIFF_MAX);   // { dg-warning "\\\[-Warray-bounds" }
+  T (ax_0.a + 1);             // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (ax_0.a + 9);             // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (ax_0.a + INT_MAX);       // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (ax_0.a + PTRDIFF_MAX);   // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
   T (ax_0.a + SIZE_MAX);      // { dg-warning "\\\[-Warray-bounds" }
 
   T (ax_0_.a);                // { dg-warning "\\\[-Warray-bounds" }
   T (ax_0_.a - 1);            // { dg-warning "\\\[-Warray-bounds" }
-  T (ax_0_.a + 1);            // { dg-warning "\\\[-Warray-bounds" }
-  T (ax_0_.a + 9);            // { dg-warning "\\\[-Warray-bounds" }
-  T (ax_0_.a + INT_MAX);      // { dg-warning "\\\[-Warray-bounds" }
-  T (ax_0_.a + PTRDIFF_MAX);  // { dg-warning "\\\[-Warray-bounds" }
+  T (ax_0_.a + 1);            // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (ax_0_.a + 9);            // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (ax_0_.a + INT_MAX);      // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  T (ax_0_.a + PTRDIFF_MAX);  // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
   T (ax_0_.a + SIZE_MAX);     // { dg-warning "\\\[-Warray-bounds" }
 }
-
diff --git a/gcc/testsuite/c-c++-common/Wrestrict.c b/gcc/testsuite/c-c++-common/Wrestrict.c
index eb074a13e7d..3b019c8a80e 100644
--- a/gcc/testsuite/c-c++-common/Wrestrict.c
+++ b/gcc/testsuite/c-c++-common/Wrestrict.c
@@ -637,9 +637,7 @@  void test_strcpy_cst (ptrdiff_t i)
   T ("012", a, a + 1);            /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" "strcpy" } */
   T ("012", a, a + 2);
   T ("012", a, a + 3);
-  /* The following doesn't overlap but it should trigger -Wstringop-overflow
-     for reading past the end.  */
-  T ("012", a, a + sizeof a);     /* { dg-warning "\\\[-Wstringop-overflow" "pr81437" { xfail *-*-* } } */
+  T ("012", a, a + sizeof a);     /* { dg-warning "\\\[-Wstringop-overread" "pr81437" } */
 
   /* The terminating nul written to d[2] overwrites s[0].  */
   T ("0123", a, a + 2);           /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" } */
diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-3.c b/gcc/testsuite/c-c++-common/attr-nonstring-3.c
index b1eb8b63004..e3ceabd0836 100644
--- a/gcc/testsuite/c-c++-common/attr-nonstring-3.c
+++ b/gcc/testsuite/c-c++-common/attr-nonstring-3.c
@@ -379,9 +379,9 @@  void test_stnrdup_warn (struct MemArrays *p)
   T (strndup (p->arr, N));
 
 
-  T (strndup (arr, N + 1));     /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 5" } */
+  T (strndup (arr, N + 1));     /* { dg-warning "specified bound 5 exceeds source size 4" } */
   T (strndup (parr, N + 1));
-  T (strndup (p->arr, N + 1));  /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 5" } */
+  T (strndup (p->arr, N + 1));  /* { dg-warning "specified bound 5 exceeds source size 4" } */
   T (strndup (p->parr, N + 1));
 }
 
diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-6.c b/gcc/testsuite/c-c++-common/attr-nonstring-6.c
index 19ceaacf39c..4b804b3f107 100644
--- a/gcc/testsuite/c-c++-common/attr-nonstring-6.c
+++ b/gcc/testsuite/c-c++-common/attr-nonstring-6.c
@@ -1,7 +1,7 @@ 
 /* PR 85623 - strncmp() warns about attribute 'nonstring' incorrectly
    in -Wstringop-overflow
   { dg-do compile }
-  { dg-options "-O2 -Wstringop-overflow -ftrack-macro-expansion=0" } */
+  { dg-options "-O2 -Wstringop-overread -ftrack-macro-expansion=0" } */
 
 #include "../gcc.dg/range.h"
 
@@ -41,15 +41,15 @@  void test_strcmp_cst (void)
   T (strcmp (S (1), a3));
   T (strcmp (S (2), a3));
   /* The following reads a3[3].  */
-  T (strcmp (S (3), a3));   /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strcmp (S (3), a3));   /* { dg-warning "\\\[-Wstringop-overread" } */
   /* The following also reads past the end of a3.  */
-  T (strcmp (S (9), a3));   /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strcmp (S (9), a3));   /* { dg-warning "\\\[-Wstringop-overread" } */
 
   T (strcmp (a3, S (0)));
   T (strcmp (a3, S (1)));
   T (strcmp (a3, S (2)));
-  T (strcmp (a3, S (3)));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strcmp (a3, S (9)));   /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strcmp (a3, S (3)));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strcmp (a3, S (9)));   /* { dg-warning "\\\[-Wstringop-overread" } */
 }
 
 
@@ -62,16 +62,16 @@  void test_strcmp_range (const char *s)
   T (strcmp (a3, s));
 
   s = signed_value () < 0 ? S (0) : S (3);
-  T (strcmp (a3, s));       /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strcmp (a3, s));       /* { dg-warning "\\\[-Wstringop-overread" } */
 
   s = signed_value () < 0 ? S (1) : S (2);
   T (strcmp (a3, s));
 
   s = signed_value () < 0 ? S (1) : S (3);
-  T (strcmp (a3, s));       /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strcmp (a3, s));       /* { dg-warning "\\\[-Wstringop-overread" } */
 
   s = signed_value () < 0 ? S (3) : S (4);
-  T (strcmp (a3, s));       /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strcmp (a3, s));       /* { dg-warning "\\\[-Wstringop-overread" } */
 }
 
 
@@ -81,21 +81,21 @@  void test_strncmp_cst (void)
   T (strncmp (S (1), a3, 2));
   T (strncmp (S (2), a3, 3));
   T (strncmp (S (3), a3, 3));
-  T (strncmp (S (3), a3, 4));   /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strncmp (S (3), a3, 4));   /* { dg-warning "\\\[-Wstringop-overread" } */
 
   T (strncmp (S (9), a3, 3));
-  T (strncmp (S (9), a3, 4));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strncmp (S (9), a3, 5));   /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strncmp (S (9), a3, 4));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strncmp (S (9), a3, 5));   /* { dg-warning "\\\[-Wstringop-overread" } */
 
   T (strncmp (a3, S (0), 1));
   T (strncmp (a3, S (1), 2));
   T (strncmp (a3, S (2), 3));
   T (strncmp (a3, S (3), 3));
-  T (strncmp (a3, S (3), 4));   /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strncmp (a3, S (3), 4));   /* { dg-warning "\\\[-Wstringop-overread" } */
 
   T (strncmp (a3, S (9), 3));
-  T (strncmp (a3, S (9), 4));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strncmp (a3, S (9), 5));   /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strncmp (a3, S (9), 4));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strncmp (a3, S (9), 5));   /* { dg-warning "\\\[-Wstringop-overread" } */
 }
 
 void test_strncmp_range (const char *s)
@@ -110,8 +110,8 @@  void test_strncmp_range (const char *s)
   T (strncmp (a3, S (5), UR (1, 4)));
   T (strncmp (a3, S (5), UR (2, 5)));
   T (strncmp (a3, S (5), UR (3, 6)));
-  T (strncmp (a3, S (5), UR (4, 7)));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strncmp (a3, S (5), UR (7, 9)));   /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strncmp (a3, S (5), UR (4, 7)));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strncmp (a3, S (5), UR (7, 9)));   /* { dg-warning "\\\[-Wstringop-overread" } */
 
   s = signed_value () < 0 ? S (0) : S (1);
   T (strncmp (a3, s, UR (1, 3)));
@@ -124,7 +124,7 @@  void test_strncmp_range (const char *s)
   T (strncmp (a3, s, UR (1, 4)));
   T (strncmp (a3, s, UR (2, 5)));
   T (strncmp (a3, s, UR (3, 6)));
-  T (strncmp (a3, s, UR (4, 7)));       /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strncmp (a3, s, UR (4, 7)));       /* { dg-warning "\\\[-Wstringop-overread" } */
 }
 
 void test_strncasecmp (void)
@@ -133,21 +133,21 @@  void test_strncasecmp (void)
   T (strncasecmp (S (1), a3, 2));
   T (strncasecmp (S (2), a3, 3));
   T (strncasecmp (S (3), a3, 3));
-  T (strncasecmp (S (3), a3, 4));   /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strncasecmp (S (3), a3, 4));   /* { dg-warning "\\\[-Wstringop-overread" } */
 
   T (strncasecmp (S (9), a3, 3));
-  T (strncasecmp (S (9), a3, 4));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strncasecmp (S (9), a3, 5));   /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strncasecmp (S (9), a3, 4));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strncasecmp (S (9), a3, 5));   /* { dg-warning "\\\[-Wstringop-overread" } */
 
   T (strncasecmp (a3, S (0), 1));
   T (strncasecmp (a3, S (1), 2));
   T (strncasecmp (a3, S (2), 3));
   T (strncasecmp (a3, S (3), 3));
-  T (strncasecmp (a3, S (3), 4));   /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strncasecmp (a3, S (3), 4));   /* { dg-warning "\\\[-Wstringop-overread" } */
 
   T (strncasecmp (a3, S (9), 3));
-  T (strncasecmp (a3, S (9), 4));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strncasecmp (a3, S (9), 5));   /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strncasecmp (a3, S (9), 4));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strncasecmp (a3, S (9), 5));   /* { dg-warning "\\\[-Wstringop-overread" } */
 }
 
 void test_strspn (void)
@@ -155,31 +155,31 @@  void test_strspn (void)
   /* strspn must traverse all characters in the second argument except
      when the first string is empty. */
   T (strspn (S (0), a3));
-  T (strspn (S (1), a3));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strspn (S (2), a3));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strspn (S (3), a3));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strspn (S (9), a3));   /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strspn (S (1), a3));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strspn (S (2), a3));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strspn (S (3), a3));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strspn (S (9), a3));   /* { dg-warning "\\\[-Wstringop-overread" } */
 
   /* Similarly, strspn must traverse all characters in the first argument
      except when the second string is empty. */
   T (strspn (a3, S (0)));
-  T (strspn (a3, S (1)));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strspn (a3, S (2)));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strspn (a3, S (3)));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strspn (a3, S (9)));   /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strspn (a3, S (1)));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strspn (a3, S (2)));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strspn (a3, S (3)));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strspn (a3, S (9)));   /* { dg-warning "\\\[-Wstringop-overread" } */
 }
 
 void test_strcspn (void)
 {
   T (strcspn (S (0), a3));
-  T (strcspn (S (1), a3));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strcspn (S (2), a3));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strcspn (S (3), a3));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strcspn (S (9), a3));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-
-  T (strcspn (a3, S (0)));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strcspn (a3, S (1)));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strcspn (a3, S (2)));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strcspn (a3, S (3)));   /* { dg-warning "\\\[-Wstringop-overflow" } */
-  T (strcspn (a3, S (9)));   /* { dg-warning "\\\[-Wstringop-overflow" } */
+  T (strcspn (S (1), a3));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strcspn (S (2), a3));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strcspn (S (3), a3));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strcspn (S (9), a3));   /* { dg-warning "\\\[-Wstringop-overread" } */
+
+  T (strcspn (a3, S (0)));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strcspn (a3, S (1)));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strcspn (a3, S (2)));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strcspn (a3, S (3)));   /* { dg-warning "\\\[-Wstringop-overread" } */
+  T (strcspn (a3, S (9)));   /* { dg-warning "\\\[-Wstringop-overread" } */
 }
diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-8.c b/gcc/testsuite/c-c++-common/attr-nonstring-8.c
index fbae8bae5f7..9ad33425b27 100644
--- a/gcc/testsuite/c-c++-common/attr-nonstring-8.c
+++ b/gcc/testsuite/c-c++-common/attr-nonstring-8.c
@@ -85,7 +85,7 @@  void test_strncat_nonstring_var (char *d, size_t n)
   T (strncat (nd3, ns5, UR (1, 2)));
   T (strncat (nd3, ns5, UR (2, 3)));
   T (strncat (nd3, ns5, UR (3, 4)));
-  T (strncat (nd3, ns5, UR (4, 5)));  /* { dg-warning "specified bound between 4 and 5 exceeds destination size 3" } */
+  T (strncat (nd3, ns5, UR (4, 5)));  /* { dg-warning "specified bound \\\[4, 5] exceeds destination size 3" } */
 
   T (strncat (nd5, ns3, UR (0, 1)));
   T (strncat (nd5, ns3, UR (1, 2)));
diff --git a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
index 635d5f40ef7..d283cbfd596 100644
--- a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
+++ b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
@@ -1,6 +1,6 @@ 
 // Test -Wsizeof-pointer-memaccess warnings.
 // { dg-do compile }
-// { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
+// { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-overread -Wno-stringop-truncation" }
 // Test just twice, once with -O0 non-fortified, once with -O2 fortified.
 // { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } }
 // { dg-skip-if "" { *-*-* }  { "-flto" } { "" } }
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-39.c b/gcc/testsuite/gcc.dg/Warray-bounds-39.c
index f10ffaca5cb..83176564bc3 100644
--- a/gcc/testsuite/gcc.dg/Warray-bounds-39.c
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-39.c
@@ -21,65 +21,65 @@  char d[4];
 
 void* test_memcpy_s0_1 (void *d)
 {
-  return memcpy (d, s0, 1);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memcpy (d, s0, 1);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 void* test_memcpy_s0_2 (void *d)
 {
-  return memcpy (d, s0, 2);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memcpy (d, s0, 2);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 void* test_memcpy_s0_0_1 (void *d)
 {
-  return memcpy (d, s0_0, 1);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memcpy (d, s0_0, 1);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 void* test_memcpy_s0_0_2 (void *d)
 {
-  return memcpy (d, s0_0, 2);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memcpy (d, s0_0, 2);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 
 void* test_memcpy_s0_1_1 (void *d)
 {
-  return memcpy (d, s0_1, 1);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memcpy (d, s0_1, 1);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 void* test_memcpy_s0_1_2 (void *d)
 {
-  return memcpy (d, s0_1, 2);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memcpy (d, s0_1, 2);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 
 void* test_memcpy_s1_0_1 (void *d)
 {
-  return memcpy (d, s1_0, 1);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memcpy (d, s1_0, 1);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 void* test_memcpy_s1_0_2 (void *d)
 {
-  return memcpy (d, s1_0, 2);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memcpy (d, s1_0, 2);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 
 void* test_memmove_s0_1 (void *d)
 {
-  return memmove (d, s0, 1);      /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memmove (d, s0, 1);      /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 void* test_memmove_s0_2 (void *d)
 {
-  return memmove (d, s0, 2);      /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memmove (d, s0, 2);      /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 void* test_memmove_s0_0_1 (void *d)
 {
-  return memmove (d, s0_0, 1);    /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memmove (d, s0_0, 1);    /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 void* test_memmove_s0_0_2 (void *d)
 {
-  return memmove (d, s0_0, 2);    /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memmove (d, s0_0, 2);    /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 
@@ -92,57 +92,57 @@  const struct Empty e1_0[1][0] = { };
 
 void* test_memcpy_e_1 (void *d)
 {
-  return memcpy (d, &e, 1);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memcpy (d, &e, 1);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 void* test_memcpy_e0_1 (void *d)
 {
-  return memcpy (d, e0, 1);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memcpy (d, e0, 1);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 void* test_memcpy_e0_0_1 (void *d)
 {
-  return memcpy (d, e0_0, 1);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memcpy (d, e0_0, 1);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 void* test_memcpy_e0_1_1 (void *d)
 {
-  return memcpy (d, e0_1, 1);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memcpy (d, e0_1, 1);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 void* test_memcpy_e1_0_1 (void *d)
 {
-  return memcpy (d, e1_0, 1);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return memcpy (d, e1_0, 1);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 
 char* test_strcpy_s0 (char *d)
 {
-  return strcpy (d, s0);          /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return strcpy (d, s0);          /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 char* test_strcpy_s0_0 (char *d)
 {
-  return strcpy (d, s0_0[0]);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return strcpy (d, s0_0[0]);     /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 
 char* test_strncpy_s0_1 (char *d)
 {
-  return strncpy (d, s0, 1);    /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return strncpy (d, s0, 1);    /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 char* test_strncpy_s0_2 (char *d)
 {
-  return strncpy (d, s0, 2);    /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return strncpy (d, s0, 2);    /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 char* test_strncpy_s0_0_1 (char *d)
 {
-  return strncpy (d, s0_0[0], 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return strncpy (d, s0_0[0], 1); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
 
 char* test_strncpy_s0_0_2 (char *d)
 {
-  return strncpy (d, s0_0[0], 2); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
+  return strncpy (d, s0_0[0], 2); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } */
 }
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-40.c b/gcc/testsuite/gcc.dg/Warray-bounds-40.c
index aabc2832b3a..c389d96a98b 100644
--- a/gcc/testsuite/gcc.dg/Warray-bounds-40.c
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-40.c
@@ -3,7 +3,7 @@ 
    functions when -Wstringop-overflow is disabled is -Warray-bounds
    with the right wording.
    { dg-do compile }
-   { dg-options "-O2 -Wall -Wno-stringop-overflow" } */
+   { dg-options "-O2 -Wall -Wno-stringop-overflow -Wno-stringop-overread" } */
 
 #define PTRDIFF_MAX   __PTRDIFF_MAX__
 #define SIZE_MAX      __SIZE_MAX__
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-58.c b/gcc/testsuite/gcc.dg/Warray-bounds-58.c
index 7bd6df2bf2e..7c469e2aefc 100644
--- a/gcc/testsuite/gcc.dg/Warray-bounds-58.c
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-58.c
@@ -15,8 +15,8 @@  void fa0_extern (void)
 {
   sink (strlen (ea0.a - 2));    // { dg-warning "\\\[-Warray-bounds" }
   sink (strlen (ea0.a - 1));    // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
-  sink (strlen (ea0.a));        // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
-  sink (strlen (ea0.a + 1));    // { dg-warning "\\\[-Warray-bounds" }
+  sink (strlen (ea0.a));        // { dg-warning "\\\[-Wstringop-overread" "pr93514" }
+  sink (strlen (ea0.a + 1));    // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
 }
 
 static struct A0 sa0 = { 0 };
@@ -25,8 +25,8 @@  void fa0_static (void)
 {
   sink (strlen (sa0.a - 2));    // { dg-warning "\\\[-Warray-bounds" }
   sink (strlen (sa0.a - 1));    // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
-  sink (strlen (sa0.a));        // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
-  sink (strlen (sa0.a + 1));    // { dg-warning "\\\[-Warray-bounds" }
+  sink (strlen (sa0.a));        // { dg-warning "\\\[-Wstringop-overread" "pr93514" }
+  sink (strlen (sa0.a + 1));    // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
 }
 
 
@@ -52,23 +52,23 @@  void fax_static (void)
   sink (strlen (ax0.a - 2));    // { dg-warning "\\\[-Warray-bounds" }
   sink (strlen (ax0.a - 1));    // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
   sink (strlen (ax0.a));
-  sink (strlen (ax0.a + 1));    // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
-  sink (strlen (ax0.a + 2));    // { dg-warning "\\\[-Warray-bounds" }
+  sink (strlen (ax0.a + 1));    // { dg-warning "\\\[-Wstringop-overread" "pr93514" }
+  sink (strlen (ax0.a + 2));    // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
 
   sink (strlen (ax1.a - 2));    // { dg-warning "\\\[-Warray-bounds" }
   sink (strlen (ax1.a - 1));    // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
   sink (strlen (ax1.a));
   sink (strlen (ax1.a + 1));
-  sink (strlen (ax1.a + 2));    // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
-  sink (strlen (ax1.a + 3));    // { dg-warning "\\\[-Warray-bounds" }
+  sink (strlen (ax1.a + 2));    // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" "pr93514" }
+  sink (strlen (ax1.a + 3));    // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
 
   sink (strlen (ax2.a - 2));    // { dg-warning "\\\[-Warray-bounds" }
   sink (strlen (ax2.a - 1));    // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
   sink (strlen (ax2.a));
   sink (strlen (ax2.a + 1));
   sink (strlen (ax2.a + 2));
-  sink (strlen (ax2.a + 3));    // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
-  sink (strlen (ax2.a + 4));    // { dg-warning "\\\[-Warray-bounds" }
+  sink (strlen (ax2.a + 3));    // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" "pr93514" }
+  sink (strlen (ax2.a + 4));    // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
 
   sink (strlen (ax3.a - 2));    // { dg-warning "\\\[-Warray-bounds" }
   sink (strlen (ax3.a - 1));    // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
@@ -76,6 +76,6 @@  void fax_static (void)
   sink (strlen (ax3.a + 1));
   sink (strlen (ax3.a + 2));
   sink (strlen (ax3.a + 3));
-  sink (strlen (ax3.a + 4));    // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
-  sink (strlen (ax3.a + 5));    // { dg-warning "\\\[-Warray-bounds" }
+  sink (strlen (ax3.a + 4));    // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" "pr93514" }
+  sink (strlen (ax3.a + 5));    // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
 }
diff --git a/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
index 4d14de28b10..5aea89a0879 100644
--- a/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
@@ -1,6 +1,6 @@ 
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-overread" } */
 /* { dg-require-effective-target alloca } */
 
 typedef __SIZE_TYPE__ size_t;
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-22.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-22.c
index a81ab99d724..8eaaa713275 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-22.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-22.c
@@ -48,11 +48,18 @@  T (rindex, b + 4, '4');     // { dg-warning "missing terminating nul" "rindex" }
 T (stpcpy, d, a);           // { dg-warning "missing terminating nul" "stpcpy" }
 
 T (stpncpy, d, a, 4);
-T (stpncpy, d, a, 5);       // { dg-warning "missing terminating nul" "stpncpy" }
+T (stpncpy, d, a, 5);       // { dg-warning "specified bound 5 exceeds the size 4 of unterminated array" "stpncpy" }
 T (stpncpy, d, a, n);
 
-T (stpncpy, d, a + n, 4);
-T (stpncpy, d, a + n, 5);   // { dg-warning "missing terminating nul" "stpncpy" }
+/* When the offset into an unterminated array isn't known and the bound
+   is less than the size of the array it suggests the access may be
+   constrained just right.  When the bound is exactly equal to the size
+   of the array, then the offset would have to be zero for the access to
+   be safe, so a warning is justified.  Otherwise, the bound is too small
+   and the access is definitely unsafe.  */
+T (stpncpy, d, a + n, 3);
+T (stpncpy, d, a + n, 4);   // { dg-warning "specified bound 4 may exceed the size of at most 4 of unterminated array" "stpncpy" }
+T (stpncpy, d, a + n, 5);   // { dg-warning "specified bound 5 exceeds the size of at most 4 of unterminated array" "stpncpy" }
 
 T (stpncpy, d, b, 4);
 T (stpncpy, d, b, 5);
@@ -67,7 +74,7 @@  T (stpncpy, d, b + 3, 5);
 T (stpncpy, d, b + 3, n);
 
 T (stpncpy, d, b + 4, 1);
-T (stpncpy, d, b + 4, 2);   // { dg-warning "missing terminating nul" "stpncpy" }
+T (stpncpy, d, b + 4, 2);   // { dg-warning "specified bound 2 exceeds the size 1 of unterminated array" "stpncpy" }
 T (stpncpy, d, b + 4, n);
 /* The following might be worth warning about since it's only safe with
    n < 4.  */
@@ -84,7 +91,7 @@  T (strcasecmp, b, b + 4);   // { dg-warning "missing terminating nul" "strcasecm
 T (strcat, d, a);           // { dg-warning "missing terminating nul" "strcat" }
 
 T (strncat, d, a, 4);
-T (strncat, d, a, 5);       // { dg-warning "missing terminating nul" "strncat" }
+T (strncat, d, a, 5);       // { dg-warning "specified bound 5 exceeds the size 4 of unterminated array" "strncat" }
 T (strncat, d, a, n);
 
 T (strncat, d, b, n);
@@ -93,7 +100,7 @@  T (strncat, d, b + 2, n);
 T (strncat, d, b + 3, n);
 T (strncat, d, b + 4, 0);
 T (strncat, d, b + 4, 1);
-T (strncat, d, b + 4, 2);   // { dg-warning "missing terminating nul" "strncat" }
+T (strncat, d, b + 4, 2);   // { dg-warning "specified bound 2 exceeds the size 1 of unterminated array" "strncat" }
 /* The following should probably trigger a warning since it's only safe
    when n < 2, makes little sense with n == 0, and not much more with
    n == 1.  */
@@ -122,8 +129,8 @@  T (strncmp, s, a, 4);
 /* The warning below is not issued because GCC folds strncmp calls with
    the same arguments to zero before it checks for the missing nul.  */
 T (strncmp, a, a, 5);       // { dg-warning "missing terminating nul" "pr92624" { xfail *-*-*} }
-T (strncmp, a, s, 5);       // { dg-warning "missing terminating nul" "strcmp" }
-T (strncmp, s, a, 5);       // { dg-warning "missing terminating nul" "strcmp" }
+T (strncmp, a, s, 5);       // { dg-warning "specified bound 5 exceeds the size 4 of unterminated array" "strcmp" }
+T (strncmp, s, a, 5);       // { dg-warning "specified bound 5 exceeds the size 4 of unterminated array" "strcmp" }
 
 T (strcpy, d, a);           // { dg-warning "missing terminating nul" "strcpy" }
 
@@ -136,10 +143,10 @@  T (strspn, s, a);           // { dg-warning "missing terminating nul" "strcspn"
 T (strdup, a);              // { dg-warning "missing terminating nul" "strdup" }
 
 T (strndup, a, 4);
-T (strndup, a, 5);          // { dg-warning "missing terminating nul" "strndup" }
+T (strndup, a, 5);          // { dg-warning "specified bound 5 exceeds the size 4 of unterminated array" "strndup" }
 T (strndup, b + 3, 2);
 T (strndup, b + 4, 1);
-T (strndup, b + 4, 2);      // { dg-warning "missing terminating nul" "strndup" }
+T (strndup, b + 4, 2);      // { dg-warning "specified bound 2 exceeds the size 1 of unterminated array" "strndup" }
 
 T (strlen, a);              // { dg-warning "missing terminating nul" "strlen" }
 
@@ -161,11 +168,12 @@  T (__stpcpy_chk, d, a, -1);           // { dg-warning "missing terminating nul"
 
 
 T (__stpncpy_chk, d, a, 4, -1);
-T (__stpncpy_chk, d, a, 5, -1);       // { dg-warning "missing terminating nul" "stpncpy_chk" }
+T (__stpncpy_chk, d, a, 5, -1);       // { dg-warning "specified bound 5 exceeds the size 4 of unterminated array" "stpncpy_chk" }
 T (__stpncpy_chk, d, a, n, -1);
 
-T (__stpncpy_chk, d, a + n, 4, -1);
-T (__stpncpy_chk, d, a + n, 5, -1);   // { dg-warning "missing terminating nul" "stpncpy_chk" }
+T (__stpncpy_chk, d, a + n, 3, -1);
+T (__stpncpy_chk, d, a + n, 4, -1);   // { dg-warning "specified bound 4 may exceed the size of at most 4 of unterminated array" "stpncpy_chk" }
+T (__stpncpy_chk, d, a + n, 5, -1);   // { dg-warning "specified bound 5 exceeds the size of at most 4 of unterminated array" "stpncpy_chk" }
 
 T (__stpncpy_chk, d, b, 4, -1);
 T (__stpncpy_chk, d, b, 5, -1);
@@ -180,16 +188,17 @@  T (__stpncpy_chk, d, b + 3, 5, -1);
 T (__stpncpy_chk, d, b + 3, n, -1);
 
 T (__stpncpy_chk, d, b + 4, 1, -1);
-T (__stpncpy_chk, d, b + 4, 2, -1);   // { dg-warning "missing terminating nul" "stpncpy_chk" }
+T (__stpncpy_chk, d, b + 4, 2, -1);   // { dg-warning "specified bound 2 exceeds the size 1 of unterminated array" "stpncpy_chk" }
 T (__stpncpy_chk, d, b + 4, n, -1);
 
 
 T (__strncat_chk, d, a, 4, -1);
-T (__strncat_chk, d, a, 5, -1);       // { dg-warning "missing terminating nul" "strncat_chk" }
+T (__strncat_chk, d, a, 5, -1);       // { dg-warning "specified bound 5 exceeds the size 4 of unterminated array" "strncat_chk" }
 T (__strncat_chk, d, a, n, -1);
 
-T (__strncat_chk, d, a + n, 4, -1);
-T (__strncat_chk, d, a + n, 5, -1);   // { dg-warning "missing terminating nul" "strncat_chk" }
+T (__strncat_chk, d, a + n, 3, -1);
+T (__strncat_chk, d, a + n, 4, -1);   // { dg-warning "specified bound 4 may exceed the size of at most 4 of unterminated array" "strncat_chk" }
+T (__strncat_chk, d, a + n, 5, -1);   // { dg-warning "specified bound 5 exceeds the size of at most 4 of unterminated array" "strncat_chk" }
 
 T (__strncat_chk, d, b, 4, -1);
 T (__strncat_chk, d, b, 5, -1);
@@ -204,16 +213,17 @@  T (__strncat_chk, d, b + 3, 5, -1);
 T (__strncat_chk, d, b + 3, n, -1);
 
 T (__strncat_chk, d, b + 4, 1, -1);
-T (__strncat_chk, d, b + 4, 2, -1);   // { dg-warning "missing terminating nul" "strncat_chk" }
+T (__strncat_chk, d, b + 4, 2, -1);   // { dg-warning "specified bound 2 exceeds the size 1 of unterminated array" "strncat_chk" }
 T (__strncat_chk, d, b + 4, n, -1);
 
 
 T (__strncpy_chk, d, a, 4, -1);
-T (__strncpy_chk, d, a, 5, -1);       // { dg-warning "missing terminating nul" "strncpy_chk" }
+T (__strncpy_chk, d, a, 5, -1);       // { dg-warning "specified bound 5 exceeds the size 4 of unterminated array" "strncpy_chk" }
 T (__strncpy_chk, d, a, n, -1);
 
-T (__strncpy_chk, d, a + n, 4, -1);
-T (__strncpy_chk, d, a + n, 5, -1);   // { dg-warning "missing terminating nul" "strncpy_chk" }
+T (__strncpy_chk, d, a + n, 3, -1);
+T (__strncpy_chk, d, a + n, 4, -1);   // { dg-warning "specified bound 4 may exceed the size of at most 4 of unterminated array" "strncpy_chk" }
+T (__strncpy_chk, d, a + n, 5, -1);   // { dg-warning "specified bound 5 exceeds the size of at most 4 of unterminated array" "strncpy_chk" }
 
 T (__strncpy_chk, d, b, 4, -1);
 T (__strncpy_chk, d, b, 5, -1);
@@ -228,7 +238,7 @@  T (__strncpy_chk, d, b + 3, 5, -1);
 T (__strncpy_chk, d, b + 3, n, -1);
 
 T (__strncpy_chk, d, b + 4, 1, -1);
-T (__strncpy_chk, d, b + 4, 2, -1);   // { dg-warning "missing terminating nul" "strncpy" }
+T (__strncpy_chk, d, b + 4, 2, -1);   // { dg-warning "specified bound 2 exceeds the size 1 of unterminated array" "strncpy" }
 T (__strncpy_chk, d, b + 4, n, -1);
 
 
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-33.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-33.c
index cb8aeb9b5d9..d4f7956c42d 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-33.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-33.c
@@ -7,7 +7,7 @@  void fcst (char *d)
 {
   char a[2] = "0";
 
-  __builtin_strcpy (d, a + 3);    // { dg-warning "\\\[-W(array-bounds|stringop-overflow)" }
+  __builtin_strcpy (d, a + 3);    // { dg-warning "\\\[-W(array-bounds|stringop-overread)" }
 }
 
 void frng (char *d, int i)
@@ -17,14 +17,14 @@  void frng (char *d, int i)
   if (i < 3)
     i = 3;
 
-  __builtin_strcpy (d, a + i);    // { dg-warning "\\\[-W(array-bounds|stringop-overflow)" }
+  __builtin_strcpy (d, a + i);    // { dg-warning "\\\[-W(array-bounds|stringop-overread)" }
 }
 
 void gcst (char *d)
 {
   char a[2] = "0";
 
-  __builtin_strcpy (d, a + 2);    // { dg-warning "\\\[-W(array-bounds|stringop-overflow)" }
+  __builtin_strcpy (d, a + 2);    // { dg-warning "\\\[-W(array-bounds|stringop-overread)" }
 }
 
 void grng (char *d, int i)
@@ -34,7 +34,7 @@  void grng (char *d, int i)
   if (i < 2)
     i = 2;
 
-  __builtin_strcpy (d, a + i);    // { dg-warning "\\\[-W(array-bounds|stringop-overflow)" }
+  __builtin_strcpy (d, a + i);    // { dg-warning "\\\[-W(array-bounds|stringop-overread)" }
 }
 
 /* { dg-prune-output "-Wuninitialized" } */
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-9.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-9.c
index 11db965c7dd..2df84b2bbd9 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-9.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-9.c
@@ -63,10 +63,10 @@  void test_strncpy (void)
 
 char* test_strndup (void)
 {
-  return strndup (s, SIZE_MAX - 5);   /* { dg-warning ".strndup. specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+ \\\[-Wstringop-overflow=\\\]" } */
+  return strndup (s, SIZE_MAX - 5);   /* { dg-warning ".strndup. specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+ \\\[-Wstringop-overread" } */
 }
 
 size_t test_strnlen (void)
 {
-  return strnlen (s, SIZE_MAX - 6);   /* { dg-warning ".strnlen. specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+ \\\[-Wstringop-overflow=\\\]" } */
+  return strnlen (s, SIZE_MAX - 6);   /* { dg-warning ".strnlen. specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+ \\\[-Wstringop-overread" } */
 }
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overread-2.c b/gcc/testsuite/gcc.dg/Wstringop-overread-2.c
new file mode 100644
index 00000000000..16dc06d968b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overread-2.c
@@ -0,0 +1,117 @@ 
+/* Verify -Wstringop-overread is issued for reading more than the maximum
+   object size but not for writing.
+  { dg-do compile }
+  { dg-options "-O2 -Wno-stringop-overflow -ftrack-macro-expansion=0" } */
+
+#define PTRDIFF_MAX   __PTRDIFF_MAX__
+#define SIZE_MAX      __SIZE_MAX__
+
+#define NOIPA         __attribute__ ((noipa))
+
+typedef __SIZE_TYPE__ size_t;
+
+void* memchr (const void*, int, size_t);
+int memcmp (const void*, const void*, size_t);
+void* memcpy (const void*, const void*, size_t);
+
+int strncmp (const char*, const char*, size_t);
+char* strncat (char*, const char*, size_t);
+char* strncpy (char*, const char*, size_t);
+size_t strnlen (const char*, size_t);
+
+void sink (int, ...);
+#define sink(...) sink (0, __VA_ARGS__)
+#define T(exp)   sink (exp)
+
+NOIPA void test_memchr (const void *p, int x)
+{
+  size_t dmax = PTRDIFF_MAX;
+  size_t smax = SIZE_MAX;
+
+  T (memchr (p, x, dmax));
+
+  T (memchr (p, x, dmax + 1));     // { dg-warning "specified bound \[0-9\]+ exceeds maximum object size" }
+  T (memchr (p, x, dmax * 2));     // { dg-warning "specified bound \[0-9\]+ exceeds maximum object size" }
+  T (memchr (p, x, smax));         // { dg-warning "\\\[-Wstringop-overread" }
+}
+
+
+NOIPA void test_memcmp (const void *p, const void *q)
+{
+  size_t dmax = PTRDIFF_MAX;
+  size_t smax = SIZE_MAX;
+
+  T (memcmp (p, q, dmax));
+
+  T (memcmp (p, q, dmax + 1));     // { dg-warning "specified bound \[0-9\]+ exceeds maximum object size" }
+  T (memcmp (p, q, dmax * 2));     // { dg-warning "specified bound \[0-9\]+ exceeds maximum object size" }
+  T (memcmp (p, q, smax));         // { dg-warning "\\\[-Wstringop-overread" }
+}
+
+
+NOIPA void test_memcpy (void *p, const void *q)
+{
+  size_t dmax = PTRDIFF_MAX;
+  size_t smax = SIZE_MAX;
+
+  T (memcpy (p, q, dmax));
+
+  T (memcpy (p, q, dmax + 1));    // -Wstringop-overflow disabled
+  T (memcpy (p, q, dmax * 2));    // ditto
+  T (memcpy (p, q, smax));        // ditto
+}
+
+
+NOIPA void test_strncmp (const char *p, const char *q)
+{
+  size_t dmax = PTRDIFF_MAX;
+  size_t smax = SIZE_MAX;
+
+  T (strncmp (p, q, dmax));
+
+  T (strncmp (p, q, dmax + 1));   // { dg-warning "specified bound \[0-9\]+ exceeds maximum object size" "strncmp" }
+  T (strncmp (p, q, dmax * 2));   // { dg-warning "\\\[-Wstringop-overread" "strncmp" }
+  T (strncmp (p, q, smax));       // { dg-warning "\\\[-Wstringop-overread" "strncmp" }
+}
+
+NOIPA void test_strncat (char *p, const char *q)
+{
+  size_t dmax = PTRDIFF_MAX;
+  size_t smax = SIZE_MAX;
+
+  T (strncat (p, q, dmax));
+
+  T (strncat (p, q, dmax + 1));   // { dg-warning "specified bound \[0-9\]+ exceeds maximum object size" }
+  T (strncat (p, q, dmax * 2));   // { dg-warning "\\\[-Wstringop-overread" }
+  T (strncat (p, q, smax));       // { dg-warning "\\\[-Wstringop-overread" }
+}
+
+NOIPA void test_strncpy (char *p, const char *q)
+{
+#if 0
+  /* Disabled: strncpy calls with an excissve bound trigger both
+     -Wstringop-overflow and, when the former option is disabled,
+     -Wstringop-overread.  The latter should probably not trigger.  */
+
+  size_t dmax = PTRDIFF_MAX;
+  size_t smax = SIZE_MAX;
+
+  T (strncpy (p, q, dmax));
+
+  T (strncpy (p, q, dmax + 1));    // -Wstringop-overflow disabled
+  T (strncpy (p, q, dmax * 2));    // ditto
+  T (strncpy (p, q, smax));        // ditto
+#endif
+}
+
+NOIPA void test_strnlen (const char *p)
+{
+  size_t dmax = PTRDIFF_MAX;
+  size_t smax = SIZE_MAX;
+
+  T (strnlen (p, dmax));
+
+  T (strnlen (p, dmax + 1));      // { dg-warning "specified bound \[0-9\]+ exceeds maximum object size" }
+  T (strnlen (p, dmax * 2));      // { dg-warning "\\\[-Wstringop-overread" }
+  T (strnlen (p, smax));          // { dg-warning "\\\[-Wstringop-overread" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overread.c b/gcc/testsuite/gcc.dg/Wstringop-overread.c
new file mode 100644
index 00000000000..51c25daa8fa
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overread.c
@@ -0,0 +1,716 @@ 
+/* Verify -Wstringop-overread is issued appropriately.
+  { dg-do compile }
+  { dg-options "-O2 -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+// <libint.h> functions.
+
+char* gettext (const char *);
+
+// <stdio.h> functions.
+
+int puts (const char*);
+int puts_unlocked (const char*);
+
+// <string.h> functions.
+
+char* strchr (const char*, int);
+
+int strcmp (const char*, const char*);
+int strncmp (const char*, const char*, size_t);
+
+char* strcat (char*, const char*);
+char* strcpy (char*, const char*);
+char* strncpy (char*, const char*, size_t);
+char* strdup (const char*);
+char* strndup (const char*, size_t);
+
+char* strpbrk (char*, const char*);
+size_t strcspn (const char*, const char*);
+size_t strspn (const char*, const char*);
+char* strstr (char*, const char*);
+
+size_t strlen (const char*);
+size_t strnlen (const char*, size_t);
+
+
+void sink (int, ...);
+#define sink(...) sink (0, __VA_ARGS__)
+
+extern char *d;
+extern char a0[0];              // { dg-message "source object 'a0'" }
+extern char a1[1];              // { dg-message "source object 'a1'" }
+extern char a2[2];              // { dg-message "source object 'a2'" }
+
+extern char b1[1];
+extern char b2[2];
+extern char bx[];
+
+const char s0[0] = { };         // { dg-message "source object 's0'" }
+const char s1[1] = "";          // { dg-message "source object 's1'" }
+const char s2[2] = "1";         // { dg-message "source object 's2'" }
+
+#define T(x) sink (0, (x))
+
+
+void test_strcat_array (const char *s, int i, int i0)
+{
+  if (i0 < 0)
+    i0 = 0;
+
+  T (strcat (d, a0));           // { dg-warning "'strcat' reading 1 or more bytes from a region of size 0" }
+  T (strcat (d, a0 + i));       // { dg-warning "'strcat' reading 1 or more bytes from a region of size 0" }
+  T (strcat (d, a0 + i + 1));   // { dg-warning "'strcat' reading 1 or more bytes from a region of size 0" }
+
+  T (strcat (d, a0 + i0));      // { dg-warning "'strcat' reading 1 or more bytes from a region of size 0" }
+
+  T (strcat (d, a1));
+  T (strcat (d, a1 + 1));       // { dg-warning "'strcat' reading 1 or more bytes from a region of size 0" }
+  T (strcat (d, a1 + i));
+  T (strcat (d, a1 + i + 1));
+
+  T (strcat (d, a1 + i0));
+  T (strcat (d, a1 + i0 + 1));  // { dg-warning "'strcat' reading 1 or more bytes from a region of size 0" }
+
+  T (strcat (d, a2));
+  T (strcat (d, a2 + 1));
+  T (strcat (d, a2 + 2));       // { dg-warning "'strcat' reading 1 or more bytes from a region of size 0" }
+  T (strcat (d, a2 + i));
+  T (strcat (d, a2 + i + 2));
+
+  T (strcat (d, a2 + i0));
+  T (strcat (d, a2 + i0 + 1));
+  T (strcat (d, a2 + i0 + 2));  // { dg-warning "'strcat' reading 1 or more bytes from a region of size 0" }
+
+  // Repeat the above with the arguments reversed.
+
+  T (strcat (a0, s));           // { dg-warning "'strcat' writing 1 or more bytes into a region of size 0" }
+  T (strcat (a0 + i, s));       // { dg-warning "'strcat' writing 1 or more bytes into a region of size 0" }
+  T (strcat (a0 + i + 1, s));   // { dg-warning "'strcat' writing 1 or more bytes into a region of size 0" }
+
+  T (strcat (a0 + i0, s));      // { dg-warning "'strcat' writing 1 or more bytes into a region of size 0" }
+
+  T (strcat (a1, s));
+  T (strcat (a1 + 1, s));       // { dg-warning "'strcat' writing 1 or more bytes into a region of size 0" }
+  T (strcat (a1 + i, s));
+  T (strcat (a1 + i + 1, s));
+
+  T (strcat (a1 + i0, s));
+  T (strcat (a1 + i0 + 1, s));  // { dg-warning "'strcat' writing 1 or more bytes into a region of size 0" }
+
+  T (strcat (a2, s));
+  T (strcat (a2 + 1, s));
+  T (strcat (a2 + 2, s));       // { dg-warning "'strcat' writing 1 or more bytes into a region of size 0" }
+  T (strcat (a2 + i, s));
+  T (strcat (a2 + i + 2, s));
+
+  T (strcat (a2 + i0, s));
+  T (strcat (a2 + i0 + 1, s));
+  T (strcat (a2 + i0 + 2, s));  // { dg-warning "'strcat' writing 1 or more bytes into a region of size 0" }
+}
+
+void test_strcat_literal (int i)
+{
+  T (strcat (d, ""));
+  T (strcat (d, "" + 0));
+  T (strcat (d, "" + i));
+
+  T (strcat (d, "1"));
+  T (strcat (d, "1" + 1));
+  T (strcat (d, "1" + 2));      // { dg-warning "'strcat' reading 1 or more bytes from a region of size 0" }
+  T (strcat (d, "1" + i));
+
+  T (strcat (d, "12"));
+  T (strcat (d, "12" + 1));
+  T (strcat (d, "12" + 2));
+  T (strcat (d, "12" + 3));     // { dg-warning "'strcat' reading 1 or more bytes from a region of size 0" }
+  T (strcat (d, "12" + i));
+}
+
+void test_strcat_string (int i)
+{
+  T (strcat (d, s0));           // { dg-warning "'strcat' reading 1 or more bytes from a region of size 0" }
+  T (strcat (d, s0 + 1));       // { dg-warning "'strcat' reading 1 or more bytes from a region of size 0" }
+  T (strcat (d, s0 + i));       // { dg-warning "'strcat' (reading 1 or more bytes from a region of size 0|argument missing terminating nul)" }
+
+  T (strcat (d, s1));
+  T (strcat (d, s1 + 1));      // { dg-warning "'strcat' reading 1 or more bytes from a region of size 0" }
+  T (strcat (d, s1 + 2));      // { dg-warning "'strcat' reading 1 or more bytes from a region of size 0" }
+  T (strcat (d, s1 + i));
+
+  T (strcat (d, s2));
+  T (strcat (d, s2 + 1));
+  T (strcat (d, s2 + 2));      // { dg-warning "'strcat' reading 1 or more bytes from a region of size 0" }
+  T (strcat (d, s2 + 3));      // { dg-warning "'strcat' reading 1 or more bytes from a region of size 0" }
+  T (strcat (d, s2 + i));
+}
+
+
+void test_strcpy_array (int i, int i0)
+{
+  if (i0 < 0)
+    i0 = 0;
+
+  T (strcpy (d, a0));           // { dg-warning "'strcpy' reading 1 or more bytes from a region of size 0" }
+  T (strcpy (d, a0 + i));       // { dg-warning "'strcpy' reading 1 or more bytes from a region of size 0" }
+  T (strcpy (d, a0 + i + 1));   // { dg-warning "'strcpy' reading 1 or more bytes from a region of size 0" }
+
+  T (strcpy (d, a0 + i0));      // { dg-warning "'strcpy' reading 1 or more bytes from a region of size 0" }
+
+  T (strcpy (d, a1));
+  T (strcpy (d, a1 + 1));       // { dg-warning "'strcpy' reading 1 or more bytes from a region of size 0" }
+  T (strcpy (d, a1 + i));
+  T (strcpy (d, a1 + i + 1));
+
+  T (strcpy (d, a1 + i0));
+  T (strcpy (d, a1 + i0 + 1));  // { dg-warning "'strcpy' reading 1 or more bytes from a region of size 0" }
+
+  T (strcpy (d, a2));
+  T (strcpy (d, a2 + 1));
+  T (strcpy (d, a2 + 2));       // { dg-warning "'strcpy' reading 1 or more bytes from a region of size 0" }
+  T (strcpy (d, a2 + i));
+  T (strcpy (d, a2 + i + 2));
+
+  T (strcpy (d, a2 + i0));
+  T (strcpy (d, a2 + i0 + 1));
+  T (strcpy (d, a2 + i0 + 2));  // { dg-warning "'strcpy' reading 1 or more bytes from a region of size 0" }
+}
+
+void test_strcpy_literal (int i)
+{
+  T (strcpy (d, ""));
+  T (strcpy (d, "" + 0));
+  T (strcpy (d, "" + i));
+
+  T (strcpy (d, "1"));
+  T (strcpy (d, "1" + 1));
+  T (strcpy (d, "1" + 2));      // { dg-warning "'strcpy' reading 1 or more bytes from a region of size 0" }
+  T (strcpy (d, "1" + i));
+
+  T (strcpy (d, "12"));
+  T (strcpy (d, "12" + 1));
+  T (strcpy (d, "12" + 2));
+  T (strcpy (d, "12" + 3));     // { dg-warning "'strcpy' reading 1 or more bytes from a region of size 0" }
+  T (strcpy (d, "12" + i));
+}
+
+void test_strcpy_string (int i)
+{
+  T (strcpy (d, s0));           // { dg-warning "'strcpy' reading 1 or more bytes from a region of size 0" }
+  T (strcpy (d, s0 + 1));       // { dg-warning "'strcpy' reading 1 or more bytes from a region of size 0" }
+  T (strcpy (d, s0 + i));       // { dg-warning "'strcpy' (reading 1 or more bytes from a region of size 0|argument missing terminating nul)" }
+
+  T (strcpy (d, s1));
+  T (strcpy (d, s1 + 1));      // { dg-warning "'strcpy' reading 1 or more bytes from a region of size 0" }
+  T (strcpy (d, s1 + 2));      // { dg-warning "'strcpy' reading 1 or more bytes from a region of size 0" }
+  T (strcpy (d, s1 + i));
+
+  T (strcpy (d, s2));
+  T (strcpy (d, s2 + 1));
+  T (strcpy (d, s2 + 2));      // { dg-warning "'strcpy' reading 1 or more bytes from a region of size 0" }
+  T (strcpy (d, s2 + 3));      // { dg-warning "'strcpy' reading 1 or more bytes from a region of size 0" }
+  T (strcpy (d, s2 + i));
+}
+
+
+void test_strncpy_array (int i)
+{
+  T (strncpy (d, a0, 0));
+  T (strncpy (d, a0, 1));       // { dg-warning "'strncpy' reading 1 byte from a region of size 0" }
+  T (strncpy (d, a0 + i, 0));
+  T (strncpy (d, a0 + i, 1));   // { dg-warning "'strncpy' reading 1 byte from a region of size 0" }
+
+  T (strncpy (d, a1, 0));
+  T (strncpy (d, a1, 1));
+  T (strncpy (d, a1 + 1, 0));
+  T (strncpy (d, a1 + 1, 1));   // { dg-warning "'strncpy' reading 1 byte from a region of size 0" }
+  T (strncpy (d, a1 + i, 0));
+  T (strncpy (d, a1 + i, 1));
+  T (strncpy (d, a1 + i, 2));
+}
+
+
+void test_strncpy_literal (int i, int i0)
+{
+  if (i0 < 0)
+    i0 = 0;
+
+  T (strncpy (d, "", 0));
+  T (strncpy (d, "", 1));
+  T (strncpy (d, "", 2));
+
+  T (strncpy (d, "" + i, 0));
+  T (strncpy (d, "" + i, 1));
+  T (strncpy (d, "" + i0, 1));
+  T (strncpy (d, "" + i0, 1));
+
+  T (strncpy (d, "" + 1, 0));
+  T (strncpy (d, "" + 1, 1));   // { dg-warning "'strncpy' reading 1 byte from a region of size 0" }
+
+  T (strncpy (d, "1", 0));
+  T (strncpy (d, "1" + 1, 0));
+  T (strncpy (d, "1" + 1, 1));
+  T (strncpy (d, "1" + 1, 2));
+  T (strncpy (d, "1" + i, 2));
+
+  T (strncpy (d, "1" + 2, 0));
+  T (strncpy (d, "1" + 2, 1));  // { dg-warning "'strncpy' reading 1 byte from a region of size 0" }
+}
+
+
+void test_strlen_array (int i, int i0)
+{
+  if (i0 < 0)
+    i0 = 0;
+
+  T (strlen (a0));              // { dg-warning "'strlen' reading 1 or more bytes from a region of size 0" }
+  T (strlen (a0 + i));          // { dg-warning "'strlen' reading 1 or more bytes from a region of size 0" }
+  T (strlen (a0 + i + 1));      // { dg-warning "'strlen' reading 1 or more bytes from a region of size 0" }
+
+  T (strlen (a0 + i0));         // { dg-warning "'strlen' reading 1 or more bytes from a region of size 0" }
+
+  T (strlen (a1));
+  T (strlen (a1 + 1));          // { dg-warning "'strlen' reading 1 or more bytes from a region of size 0" }
+  T (strlen (a1 + i));
+  T (strlen (a1 + i + 1));
+
+  T (strlen (a1 + i0));
+  T (strlen (a1 + i0 + 1));     // { dg-warning "'strlen' reading 1 or more bytes from a region of size 0" }
+
+  T (strlen (a2));
+  T (strlen (a2 + 1));
+  T (strlen (a2 + 2));          // { dg-warning "'strlen' reading 1 or more bytes from a region of size 0" }
+  T (strlen (a2 + i));
+  T (strlen (a2 + i + 2));
+
+  T (strlen (a2 + i0));
+  T (strlen (a2 + i0 + 1));
+  T (strlen (a2 + i0 + 2));     // { dg-warning "'strlen' reading 1 or more bytes from a region of size 0" }
+}
+
+
+void test_strnlen_array (int i, int i0, unsigned n)
+{
+  if (i0 < 0)
+    i0 = 0;
+
+  T (strnlen (a0, 0));
+  T (strnlen (a0, 1));          // { dg-warning "'strnlen' (reading 1 byte from a region of size 0|specified bound 1 exceeds source size 0)" }
+  T (strnlen (a0, i0));
+  T (strnlen (a0, i0 + 1));     // { dg-warning "'strnlen' (reading between 1 and \[0-9\]+ bytes from a region of size 0|specified bound \\\[1, \[0-9\]+\\\] exceeds source size 0)" }
+  T (strnlen (a0, n));
+  T (strnlen (a0 + i, 0));
+  T (strnlen (a0 + i, 1));      // { dg-warning "'strnlen' (reading 1 byte from a region of size 0|specified bound 1 exceeds source size 0)" }
+  T (strnlen (a0 + i, i0));
+  T (strnlen (a0 + i, n));
+  T (strnlen (a0 + i + 1, 0));
+  T (strnlen (a0 + i + 1, 1));  // { dg-warning "'strnlen' (reading 1 byte from a region of size 0|specified bound 1 exceeds source size 0)" }
+
+  T (strnlen (a0 + i0, 0));
+  T (strnlen (a0 + i0, 1));     // { dg-warning "'strnlen' (reading 1 byte from a region of size 0|specified bound 1 exceeds source size 0)" }
+  T (strnlen (a0 + i0, n));
+
+  T (strnlen (a1, 0));
+  T (strnlen (a1, 1));
+  T (strnlen (a1, 2));          // { dg-warning "'strnlen' specified bound 2 exceeds source size 1" "pr?????" { xfail *-*-* } }
+  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, 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, n));
+  T (strnlen (a1 + i, 0));
+  T (strnlen (a1 + i, 1));
+  T (strnlen (a1 + i, 2));      // { dg-warning "'strnlen' specified bound 2 exceeds source size 1" }
+  T (strnlen (a1 + i, n));
+  T (strnlen (a1 + i + 1, 0));
+  T (strnlen (a1 + i + 1, 1));
+  T (strnlen (a1 + i + 1, 2));  // { dg-warning "'strnlen' specified bound 2 exceeds source size 1" }
+  T (strnlen (a1 + i + 1, n));
+
+  T (strnlen (a1 + i0, 0));
+  T (strnlen (a1 + i0, 1));
+  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, n));
+
+  T (strnlen (a2, 0));
+  T (strnlen (a2, 1));
+  T (strnlen (a2, 2));
+  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"  "pr?????" { xfail *-*-* } }
+  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, n));
+  T (strnlen (a2 + i, 0));
+  T (strnlen (a2 + i, 1));
+  T (strnlen (a2 + i, 2));
+  T (strnlen (a2 + i + 2, 0));
+  T (strnlen (a2 + i + 2, 1));
+  T (strnlen (a2 + i + 2, 2));
+  T (strnlen (a2 + i + 2, n));
+
+  T (strnlen (a2 + i0, 0));
+  T (strnlen (a2 + i0, 1));
+  T (strnlen (a2 + i0, 2));
+  T (strnlen (a2 + i0, 3));     // { dg-warning "'strnlen' specified bound 3 exceeds source size 2" }
+  T (strnlen (a2 + i0, n));
+
+  T (strnlen (a2 + i0 + 1, 0));
+  T (strnlen (a2 + i0 + 1, 1));
+  T (strnlen (a2 + i0 + 1, 2));
+  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, 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, n));
+}
+
+
+void test_strcmp_array (const char *s, int i)
+{
+  T (strcmp (a0, ""));        // { dg-warning "'strcmp' reading 1 or more bytes from a region of size 0" "pr?????" { xfail *-*-* } }
+
+  T (strcmp (a0, s));         // { dg-warning "'strcmp' reading 1 or more bytes from a region of size 0" }
+  T (strcmp (a0 + i, s));     // { dg-warning "'strcmp' reading 1 or more bytes from a region of size 0" }
+
+  T (strcmp (a1, s));
+  T (strcmp (a1 + 1, s));     // { dg-warning "'strcmp' reading 1 or more bytes from a region of size 0" }
+  T (strcmp (a1 + i, s));
+  T (strcmp (a1 + i + 1, s));
+
+
+  // Repeat the above with the arguments reversed.
+
+  T (strcmp ("", a0));         // { dg-warning "'strcmp' reading 1 or more bytes from a region of size 0" "pr?????" { xfail *-*-*} }
+
+  T (strcmp (s, a0));         // { dg-warning "'strcmp' reading 1 or more bytes from a region of size 0" }
+  T (strcmp (s, a0 + i));     // { dg-warning "'strcmp' reading 1 or more bytes from a region of size 0" }
+
+  T (strcmp (s, a1));
+  T (strcmp (s, a1 + 1));     // { dg-warning "'strcmp' reading 1 or more bytes from a region of size 0" }
+  T (strcmp (s, a1 + i));
+  T (strcmp (s, a1 + i + 1));
+}
+
+/* The number of characters read is considered to be bounded not just
+   by the third argument to strncmp but also by the length of the shorter
+   of the two strings.  When the string length is unknowm, verify that
+   a warning is only issued for certain reading past the end but not
+   otherwise.  */
+
+void test_strncmp_array (const char *s, int i)
+{
+  T (strncmp (a0, a0, 0));
+
+  T (strncmp (a0, s, 0));
+  T (strncmp (a0, s, 1));       // { dg-warning "'strncmp' reading 1 or more bytes from a region of size 0" "pr?????" { xfail *-*-* } }
+
+  T (strncmp (a0, s, 2));       // { dg-warning "'strncmp' (reading between 1 and 2 bytes from a region of size 0|specified bound 2 exceeds source size 0)" }
+  T (strncmp (a1, s, 0));
+  T (strncmp (a1, s, 1));
+  T (strncmp (a1 + 1, s, 1));   // { dg-warning "'strncmp' reading 1 byte from a region of size 0" "pr?????" { xfail *-*-*} }
+  T (strncmp (a1, s, 1));
+  T (strncmp (a1 + 1, s, 2));   // { dg-warning "'strncmp' (reading between 1 and 2 bytes from a region of size 0|specified bound 2 exceeds source size 0)" }
+
+  T (strncmp (a2, s, 1));
+  T (strncmp (a2, s, 2));
+  T (strncmp (a2, s, 3));
+
+  T (strncmp (a2 + 1, s, 1));
+  T (strncmp (a2 + 2, s, 2));   // { dg-warning "'strncmp' (reading between 1 and 2 bytes from a region of size 0|specified bound 2 exceeds source size 0)" }
+
+  T (strncmp (a1, b1, 0));
+  T (strncmp (a1, b1, 1));
+  T (strncmp (a1, b1, 2));      // { dg-warning "'strncmp' specified bound 2 exceeds source size 1" }
+}
+
+
+void test_strncmp_literal (const char *s, int i)
+{
+  T (strncmp (a0, "", 0));
+  T (strncmp (a0, "1", 0));
+  T (strncmp (a0, "12", 0));
+
+  /* The calls with a bound in excess of the length of the literal are
+     folded early (most into strcmp) so the warning doesn't trigger.  */
+  T (strncmp (s, "", 0));
+
+  T (strncmp (s, "1", 0));
+  T (strncmp (s, "1", 1));
+  T (strncmp (s, "1", 2));      // { dg-warning "\\\[-Wstringop-overread" "pr?????" { xfail *-*-* } }
+
+  T (strncmp (s, "12", 0));
+  T (strncmp (s, "12", 1));
+  T (strncmp (s, "12", 2));
+  T (strncmp (s, "12", 3));     // { dg-warning "\\\[-Wstringop-overread" "pr?????" { xfail *-*-* } }
+
+  T (strncmp (s, "123", 0));
+  T (strncmp (s, "123", 1));
+  T (strncmp (s, "123", 2));
+  T (strncmp (s, "123", 3));
+  T (strncmp (s, "123", 4));    // { dg-warning "\\\[-Wstringop-overread" "pr?????" { xfail *-*-* } }
+}
+
+
+void test_strchr_array (int x, int i)
+{
+  T (strchr (a0, x));         // { dg-warning "'strchr' reading 1 or more bytes from a region of size 0" }
+  T (strchr (a0 + i, x));     // { dg-warning "'strchr' reading 1 or more bytes from a region of size 0" }
+
+  T (strchr (a1, x));
+  T (strchr (a1 + 1, x));     // { dg-warning "'strchr' reading 1 or more bytes from a region of size 0" }
+  T (strchr (a1 + i, x));
+  T (strchr (a1 + i + 1, x));
+}
+
+
+void test_strdup_array (int i)
+{
+  T (strdup (a0));            // { dg-warning "'strdup' reading 1 or more bytes from a region of size 0" }
+  T (strdup (a0 + i));        // { dg-warning "'strdup' reading 1 or more bytes from a region of size 0" }
+
+  T (strdup (a1));
+  T (strdup (a1 + 1));        // { dg-warning "'strdup' reading 1 or more bytes from a region of size 0" }
+  T (strdup (a1 + i));
+  T (strdup (a1 + i + 1));
+}
+
+
+void test_strndup_array (int i, int i0, unsigned n)
+{
+  if (i0 < 0)
+    i0 = 0;
+
+  T (strndup (a0, 0));
+  T (strndup (a0, 1));          // { dg-warning "'strndup' (reading 1 byte from a region of size 0|specified bound 1 exceeds source size 0)" }
+  T (strndup (a0, i0));
+  T (strndup (a0, i0 + 1));     // { dg-warning "'strndup' (reading between 1 and \[0-9\]+ bytes from a region of size 0|specified bound \\\[1, \[0-9\]+\\\] exceeds source size 0)" }
+  T (strndup (a0, n));
+  T (strndup (a0 + i, 0));
+  T (strndup (a0 + i, 1));      // { dg-warning "'strndup' (reading 1 byte from a region of size 0|specified bound 1 exceeds source size 0)" }
+  T (strndup (a0 + i, i0));
+  T (strndup (a0 + i, n));
+  T (strndup (a0 + i + 1, 0));
+  T (strndup (a0 + i + 1, 1));  // { dg-warning "'strndup' (reading 1 byte from a region of size 0|specified bound 1 exceeds source size 0)" }
+
+  T (strndup (a0 + i0, 0));
+  T (strndup (a0 + i0, 1));     // { dg-warning "'strndup' (reading 1 byte from a region of size 0|specified bound 1 exceeds source size 0)" }
+  T (strndup (a0 + i0, n));
+
+  T (strndup (a1, 0));
+  T (strndup (a1, 1));
+  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, 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, n));
+  T (strndup (a1 + i, 0));
+  T (strndup (a1 + i, 1));
+  T (strndup (a1 + i, 2));      // { dg-warning "'strndup' specified bound 2 exceeds source size 1" }
+  T (strndup (a1 + i, n));
+  T (strndup (a1 + i + 1, 0));
+  T (strndup (a1 + i + 1, 1));
+  T (strndup (a1 + i + 1, 2));  // { dg-warning "'strndup' specified bound 2 exceeds source size 1" }
+  T (strndup (a1 + i + 1, n));
+
+  T (strndup (a1 + i0, 0));
+  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, n));
+
+  T (strndup (a2, 0));
+  T (strndup (a2, 1));
+  T (strndup (a2, 2));
+  T (strndup (a2, n));
+  T (strndup (a2 + 1, 0));
+  T (strndup (a2 + 1, 1));
+  T (strndup (a2 + 1, 2));
+  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, n));
+  T (strndup (a2 + i, 0));
+  T (strndup (a2 + i, 1));
+  T (strndup (a2 + i, 2));
+  T (strndup (a2 + i + 2, 0));
+  T (strndup (a2 + i + 2, 1));
+  T (strndup (a2 + i + 2, 2));
+  T (strndup (a2 + i + 2, n));
+
+  T (strndup (a2 + i0, 0));
+  T (strndup (a2 + i0, 1));
+  T (strndup (a2 + i0, 2));
+  T (strndup (a2 + i0, 3));     // { dg-warning "'strndup' specified bound 3 exceeds source size 2" }
+  T (strndup (a2 + i0, n));
+
+  T (strndup (a2 + i0 + 1, 0));
+  T (strndup (a2 + i0 + 1, 1));
+  T (strndup (a2 + i0 + 1, 2));
+  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, 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, n));
+}
+
+
+void test_strpbrk_array (char *s, int i)
+{
+  T (strpbrk (a0, ""));       // { dg-warning "'strpbrk' reading 1 or more bytes from a region of size 0" "pr?????" { xfail *-*-* } }
+
+  T (strpbrk (a0, s));        // { dg-warning "'strpbrk' reading 1 or more bytes from a region of size 0" }
+  T (strpbrk (a0 + i, s));    // { dg-warning "'strpbrk' reading 1 or more bytes from a region of size 0" }
+
+  T (strpbrk (a1, s));
+  T (strpbrk (a1 + 1, s));    // { dg-warning "'strpbrk' reading 1 or more bytes from a region of size 0" }
+  T (strpbrk (a1 + i, s));
+  T (strpbrk (a1 + i + 1, s));
+
+
+  // Repeat the above with the arguments reversed.
+
+  T (strpbrk ("", a0));       // { dg-warning "'strpbrk' reading 1 or more bytes from a region of size 0" }
+
+  T (strpbrk (s, a0));        // { dg-warning "'strpbrk' reading 1 or more bytes from a region of size 0" }
+  T (strpbrk (s, a0 + i));    // { dg-warning "'strpbrk' reading 1 or more bytes from a region of size 0" }
+
+  T (strpbrk (s, a1));
+  T (strpbrk (s, a1 + 1));    // { dg-warning "'strpbrk' reading 1 or more bytes from a region of size 0" }
+  T (strpbrk (s, a1 + i));
+  T (strpbrk (s, a1 + i + 1));
+}
+
+
+void test_strspn_array (const char *s, int i)
+{
+  T (strspn (a0, ""));        // { dg-warning "'strspn' reading 1 or more bytes from a region of size 0" "pr?????" { xfail *-*-* } }
+
+  T (strspn (a0, s));         // { dg-warning "'strspn' reading 1 or more bytes from a region of size 0" }
+  T (strspn (a0 + i, s));     // { dg-warning "'strspn' reading 1 or more bytes from a region of size 0" }
+
+  T (strspn (a1, s));
+  T (strspn (a1 + 1, s));     // { dg-warning "'strspn' reading 1 or more bytes from a region of size 0" }
+  T (strspn (a1 + i, s));
+  T (strspn (a1 + i + 1, s));
+
+
+  // Repeat the above with the arguments reversed.
+
+  T (strspn ("", a0));         // { dg-warning "'strspn' reading 1 or more bytes from a region of size 0" "pr?????" { xfail *-*-*} }
+
+  T (strspn (s, a0));         // { dg-warning "'strspn' reading 1 or more bytes from a region of size 0" }
+  T (strspn (s, a0 + i));     // { dg-warning "'strspn' reading 1 or more bytes from a region of size 0" }
+
+  T (strspn (s, a1));
+  T (strspn (s, a1 + 1));     // { dg-warning "'strspn' reading 1 or more bytes from a region of size 0" }
+  T (strspn (s, a1 + i));
+  T (strspn (s, a1 + i + 1));
+}
+
+
+void test_strcspn_array (const char *s, int i)
+{
+  /* The call below is tranformed to strlen() so the warning references
+     the latter function instead of strcspn.  Avoid testing that aspect.  */
+  T (strcspn (a0, ""));       // { dg-warning "reading 1 or more bytes from a region of size 0" }
+
+  T (strcspn (a0, s));        // { dg-warning "'strcspn' reading 1 or more bytes from a region of size 0" }
+  T (strcspn (a0 + i, s));    // { dg-warning "'strcspn' reading 1 or more bytes from a region of size 0" }
+
+  T (strcspn (a1, s));
+  T (strcspn (a1 + 1, s));    // { dg-warning "'strcspn' reading 1 or more bytes from a region of size 0" }
+  T (strcspn (a1 + i, s));
+  T (strcspn (a1 + i + 1, s));
+
+
+  // Repeat the above with the arguments reversed.
+
+  T (strcspn ("", a0));       // { dg-warning "'strcspn' reading 1 or more bytes from a region of size 0" "pr?????" { xfail *-*-*} }
+
+  T (strcspn (s, a0));        // { dg-warning "'strcspn' reading 1 or more bytes from a region of size 0" }
+  T (strcspn (s, a0 + i));    // { dg-warning "'strcspn' reading 1 or more bytes from a region of size 0" }
+
+  T (strcspn (s, a1));
+  T (strcspn (s, a1 + 1));    // { dg-warning "'strcspn' reading 1 or more bytes from a region of size 0" }
+  T (strcspn (s, a1 + i));
+  T (strcspn (s, a1 + i + 1));
+}
+
+
+void test_strstr_array (char *s, int i)
+{
+  T (strstr (a0, ""));        // { dg-warning "'strstr' reading 1 or more bytes from a region of size 0" "pr?????" { xfail *-*-* } }
+
+  T (strstr (a0, s));         // { dg-warning "'strstr' reading 1 or more bytes from a region of size 0" }
+  T (strstr (a0 + i, s));     // { dg-warning "'strstr' reading 1 or more bytes from a region of size 0" }
+
+  T (strstr (a1, s));
+  T (strstr (a1 + 1, s));     // { dg-warning "'strstr' reading 1 or more bytes from a region of size 0" }
+  T (strstr (a1 + i, s));
+  T (strstr (a1 + i + 1, s));
+
+
+  // Repeat the above with the arguments reversed.
+
+  T (strstr ("", a0));        // { dg-warning "'strstr' reading 1 or more bytes from a region of size 0" }
+
+  T (strstr (s, a0));         // { dg-warning "'strstr' reading 1 or more bytes from a region of size 0" }
+  T (strstr (s, a0 + i));     // { dg-warning "'strstr' reading 1 or more bytes from a region of size 0" }
+
+  T (strstr (s, a1));
+  T (strstr (s, a1 + 1));     // { dg-warning "'strstr' reading 1 or more bytes from a region of size 0" }
+  T (strstr (s, a1 + i));
+  T (strstr (s, a1 + i + 1));
+}
+
+
+void test_puts_array (int i)
+{
+  T (puts (a0));              // { dg-warning "'puts' reading 1 or more bytes from a region of size 0" }
+  T (puts (a0 + i));          // { dg-warning "'puts' reading 1 or more bytes from a region of size 0" }
+
+  T (puts (a1));
+  T (puts (a1 + 1));          // { dg-warning "'puts' reading 1 or more bytes from a region of size 0" }
+  T (puts (a1 + i));
+  T (puts (a1 + i + 1));
+}
+
+
+void test_puts_unlocked_array (int i)
+{
+  T (puts_unlocked (a0));     // { dg-warning "'puts_unlocked' reading 1 or more bytes from a region of size 0" }
+  T (puts_unlocked (a0 + i)); // { dg-warning "'puts_unlocked' reading 1 or more bytes from a region of size 0" }
+
+  T (puts_unlocked (a1));
+  T (puts_unlocked (a1 + 1)); // { dg-warning "'puts_unlocked' reading 1 or more bytes from a region of size 0" }
+  T (puts_unlocked (a1 + i));
+  T (puts_unlocked (a1 + i + 1));
+}
+
+
+void test_gettext_array (int i)
+{
+  T (gettext (a0));           // { dg-warning "'gettext' reading 1 or more bytes from a region of size 0" }
+  T (gettext (a0 + i));       // { dg-warning "'gettext' reading 1 or more bytes from a region of size 0" }
+
+  T (gettext (a1));
+  T (gettext (a1 + 1));       // { dg-warning "'gettext' reading 1 or more bytes from a region of size 0" }
+  T (gettext (a1 + i));
+  T (gettext (a1 + i + 1));
+}
diff --git a/gcc/testsuite/gcc.dg/attr-nonstring-2.c b/gcc/testsuite/gcc.dg/attr-nonstring-2.c
index ef2144d6207..ba4757d673f 100644
--- a/gcc/testsuite/gcc.dg/attr-nonstring-2.c
+++ b/gcc/testsuite/gcc.dg/attr-nonstring-2.c
@@ -26,8 +26,8 @@  void test_strnlen_array_cst (void)
   T (strnlen (ns3, 1));
   T (strnlen (ns3, 2));
   T (strnlen (ns3, 3));
-  T (strnlen (ns3, 4));             /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 4" } */
-  T (strnlen (ns3, DIFF_MAX));      /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \[0-9\]+" } */
+  T (strnlen (ns3, 4));             /* { dg-warning "specified bound 4 exceeds source size 3" } */
+  T (strnlen (ns3, DIFF_MAX));      /* { dg-warning "specified bound \[0-9\]+ exceeds source size" } */
   T (strnlen (ns3, SIZE_MAX));      /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 
   NONSTRING char ns5[5];
@@ -37,8 +37,8 @@  void test_strnlen_array_cst (void)
   T (strnlen (ns5, 1));
   T (strnlen (ns5, 2));
   T (strnlen (ns5, 3));
-  T (strnlen (ns5, 6));             /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 6" } */
-  T (strnlen (ns5, DIFF_MAX));      /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \[0-9\]+" } */
+  T (strnlen (ns5, 6));             /* { dg-warning "specified bound 6 exceeds source size 5" } */
+  T (strnlen (ns5, DIFF_MAX));      /* { dg-warning "specified bound \[0-9\]+ exceeds source size 5" } */
   T (strnlen (ns5, SIZE_MAX));      /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 }
 
@@ -52,8 +52,8 @@  void test_strnlen_array_range (void)
   T (strnlen (ns3, UR (0, 9)));
   T (strnlen (ns3, UR (3, 4)));
   T (strnlen (ns3, UR (3, DIFF_MAX)));
-  T (strnlen (ns3, UR (4, 5)));     /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[4, 5]" } */
-  T (strnlen (ns3, UR (DIFF_MAX, SIZE_MAX)));  /* { dg-warning "argument 1 declared attribute .nonstring. is smaller " } */
+  T (strnlen (ns3, UR (4, 5)));     /* { dg-warning "specified bound \\\[4, 5] exceeds source size 3" } */
+  T (strnlen (ns3, UR (DIFF_MAX, SIZE_MAX)));  /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds source size 3 " } */
 }
 
 
@@ -73,8 +73,8 @@  void test_strnlen_string_cst (void)
   T (3, "12",  3, 1);
   T (3, "12",  3, 9);
   T (3, "123", 3, 1);
-  T (3, "123", 3, 4);               /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 4" } */
-  T (3, "123", 3, 9);               /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 9" } */
+  T (3, "123", 3, 4);               /* { dg-warning "specified bound 4 exceeds source size 3" } */
+  T (3, "123", 3, 9);               /* { dg-warning "specified bound 9 exceeds source size 3" } */
 
   T (5, "1",   2, 1);
   T (5, "1",   2, 2);
@@ -84,7 +84,7 @@  void test_strnlen_string_cst (void)
   T (5, "12",  3, 9);
   T (5, "123", 3, 1);
   T (5, "123", 3, 5);
-  T (5, "123", 3, 6);               /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 6" } */
+  T (5, "123", 3, 6);               /* { dg-warning "specified bound 6 exceeds source size 5" } */
 
   /* Strnlen shouldn't trigger a warning for arrays of unknown size
      (except for accesses to uninitialized elements when those are
@@ -110,6 +110,6 @@  void test_strnlen_string_range (void)
 {
   T (3, "1",   2, UR (0, 1));
   T (3, "1",   2, UR (3, 9));
-  T (3, "123", 3, UR (4, 5));       /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[4, 5]" } */
-  T (3, "123", 3, UR (5, 9));       /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[5, 9]" } */
+  T (3, "123", 3, UR (4, 5));       /* { dg-warning "specified bound \\\[4, 5] exceeds source size 3" } */
+  T (3, "123", 3, UR (5, 9));       /* { dg-warning "specified bound \\\[5, 9] exceeds source size 3" } */
 }
diff --git a/gcc/testsuite/gcc.dg/attr-nonstring-3.c b/gcc/testsuite/gcc.dg/attr-nonstring-3.c
index 4a104504555..34f31fb6aa7 100644
--- a/gcc/testsuite/gcc.dg/attr-nonstring-3.c
+++ b/gcc/testsuite/gcc.dg/attr-nonstring-3.c
@@ -37,15 +37,21 @@  void strncmp_cst (void)
   T (STR, /* [] */, STR, /* [] */, n);
   T (STR, /* [] */, STR, /* [] */, n + 1);    /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 
+  T (STR, 1, STR, /* [] */, 1);
   T (STR, 1, STR, /* [] */, n);
   T (STR, 2, STR, /* [] */, n + 1);           /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 
+  T (STR, /* [] */, STR, 3, 3);
   T (STR, /* [] */, STR, 3, n);
   T (STR, /* [] */, STR, 4, n + 1);           /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 
+  T (STR, /* [] */, NS, /* [] */, 3);
   T (STR, /* [] */, NS, /* [] */, n);
   T (STR, /* [] */, NS, /* [] */, n + 1);     /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 
+  T (STR, 5, NS, /* [] */, 4);
+  T (STR, 5, NS, /* [] */, 5);
+  T (STR, 5, NS, /* [] */, 6);
   T (STR, 5, NS, /* [] */, n);
   T (STR, 6, NS, /* [] */, n + 1);            /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 
@@ -56,19 +62,22 @@  void strncmp_cst (void)
   T (NS, /* [] */, STR, /* [] */, n);
   T (NS, /* [] */, STR, /* [] */, n + 1);     /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 
-  T (NS, 9, STR, /* [] */, n);                /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */
+  T (NS, 9, STR, /* [] */, n);                /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound \[0-9\]+" } */
   T (NS, 10, STR, /* [] */, n + 1);           /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 
+  T (NS, /* [] */, STR, 11, 11);
   T (NS, /* [] */, STR, 11, n);
   T (NS, /* [] */, STR, 12, n + 1);           /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 
   T (NS, /* [] */, NS, /* [] */, n);
   T (NS, /* [] */, NS, /* [] */, n + 1);      /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 
-  T (NS, 13, NS, /* [] */, n);                /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */
+  T (NS, 13, NS, /* [] */, 13);
+  T (NS, 13, NS, /* [] */, n);                /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound \[0-9\]+" } */
   T (NS, 14, NS, /* [] */, n + 1);            /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 
-  T (NS, /* [] */, NS, 15, n);                /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound" } */
+  T (NS, /* [] */, NS, 15, 15);
+  T (NS, /* [] */, NS, 15, 16);               /* { dg-warning "argument 2 declared attribute 'nonstring' is smaller than the specified bound 16" } */
   T (NS, /* [] */, NS, 16, n + 1);            /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 }
 
@@ -81,6 +90,7 @@  void strncmp_range (void)
   T (STR, /* [] */, STR, /* [] */, n);
   T (STR, /* [] */, STR, /* [] */, n + 1);    /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
 
+  T (STR, 1, STR, /* [] */, 1);
   T (STR, 1, STR, /* [] */, n);
   T (STR, 2, STR, /* [] */, n + 1);           /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
 
@@ -93,7 +103,7 @@  void strncmp_range (void)
   T (STR, 5, NS, /* [] */, n);
   T (STR, 6, NS, /* [] */, n + 1);            /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
 
-  T (STR, /* [] */, NS, 7, n);                /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound" } */
+  T (STR, /* [] */, NS, 7, n);                /* { dg-warning "argument 2 declared attribute 'nonstring' is smaller than the specified bound \\\[\[0-9\]+, \[0-9\]+]" } */
 
   T (STR, /* [] */, NS, 8, n + 1);            /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
 
diff --git a/gcc/testsuite/gcc.dg/attr-nonstring-4.c b/gcc/testsuite/gcc.dg/attr-nonstring-4.c
index 7daff9754c7..f2416c16e83 100644
--- a/gcc/testsuite/gcc.dg/attr-nonstring-4.c
+++ b/gcc/testsuite/gcc.dg/attr-nonstring-4.c
@@ -40,7 +40,7 @@  void strnlen_cst (void)
   T (NS, /* [] */, n);
   T (NS, /* [] */, n + 1);     /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 
-  T (NS, 9, n);                /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */
+  T (NS, 9, n);                /* { dg-warning "specified bound \[0-9\]+ exceeds source size 9" } */
   T (NS, 10, n + 1);           /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
 }
 
@@ -53,12 +53,12 @@  void strnlen_range (void)
   T (STR, /* [] */, n);
   T (STR, /* [] */, n + 1);    /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
 
-  T (STR, 1, n);
+  T (STR, 1, n);               /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds source size 1" } */
   T (STR, 2, n + 1);           /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
 
   T (NS, /* [] */, n);
   T (NS, /* [] */, n + 1);     /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
 
-  T (NS, 9, n);                /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */
+  T (NS, 9, n);                /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds source size 9" } */
   T (NS, 10, n + 1);           /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
 }
diff --git a/gcc/testsuite/gcc.dg/attr-nonstring.c b/gcc/testsuite/gcc.dg/attr-nonstring.c
index 46f5c0d7c59..d93e93ca676 100644
--- a/gcc/testsuite/gcc.dg/attr-nonstring.c
+++ b/gcc/testsuite/gcc.dg/attr-nonstring.c
@@ -44,80 +44,80 @@  int strcmp_nonstring_1 (NONSTRING const char *a, const char *b)
      no good on its own.  Use dg-regexp instead to verify that just
      one instance of the warning is issued.  See gcc.dg/pr64223-1
      for a different approach.  */
-  return strcmp (a, b);  /* { dg-regexp "\[^\n\r\]+: warning: .strcmp. argument 1 declared attribute .nonstring. \\\[-Wstringop-overflow=]" "strcmp" } */
+  return strcmp (a, b);  /* { dg-regexp "\[^\n\r\]+: warning: .strcmp. argument 1 declared attribute .nonstring. \\\[-Wstringop-overread\[^\n\r\]*" "strcmp" } */
 }
 
 int strcmp_nonstring_2 (const char *a, NONSTRING const char *b)
 {
-  return strcmp (a, b);  /* { dg-regexp "\[^\n\r\]+: warning: .strcmp. argument 2 declared attribute .nonstring. \\\[-Wstringop-overflow=]" "strcmp" } */
+  return strcmp (a, b);  /* { dg-regexp "\[^\n\r\]+: warning: .strcmp. argument 2 declared attribute .nonstring. \\\[-Wstringop-overread\[^\n\r\]*" "strcmp" } */
 }
 
 int strncmp_nonstring_1 (const char *s)
 {
-  return strncmp (s, ns5, sizeof ns5 + 1);  /* { dg-regexp "\[^\n\r\]+: warning: .strncmp. argument 2 declared attribute .nonstring. \[^\n\r\]+ \\\[-Wstringop-overflow=]" "strncmp" } */
+  return strncmp (s, ns5, sizeof ns5 + 1);  /* { dg-regexp "\[^\n\r\]+: warning: .strncmp. argument 2 declared attribute .nonstring. \[^\n\r\]+ \\\[-Wstringop-overread\[^\n\r\]*" "strncmp" } */
 }
 
 int strncmp_nonstring_2 (const char *s)
 {
-  return strncmp (ns5, s, sizeof ns5 + 1);  /* { dg-regexp "\[^\n\r\]+: warning: .strncmp. argument 1 declared attribute .nonstring. \[^\n\r\]+ \\\[-Wstringop-overflow=]" "strncmp" } */
+  return strncmp (ns5, s, sizeof ns5 + 1);  /* { dg-regexp "\[^\n\r\]+: warning: .strncmp. argument 1 declared attribute .nonstring. \[^\n\r\]+ \\\[-Wstringop-overread\[^\n\r\]*" "strncmp" } */
 }
 
 char* stpcpy_nonstring (char *d, NONSTRING const char *s)
 {
-  return stpcpy (d, s);  /* { dg-regexp "\[^\n\r\]+: warning: .stpcpy. argument 2 declared attribute .nonstring. \\\[-Wstringop-overflow=]" "stpcpy" } */
+  return stpcpy (d, s);  /* { dg-regexp "\[^\n\r\]+: warning: .stpcpy. argument 2 declared attribute .nonstring. \\\[-Wstringop-overread\[^\n\r\]*" "stpcpy" } */
 }
 
 char* stpncpy_nonstring (char *d)
 {
-  return stpncpy (d, ns5, sizeof ns5 + 1);  /* { dg-regexp "\[^\n\r\]+: warning: .stpncpy. argument 2 declared attribute .nonstring. \[^\n\r\]+ \\\[-Wstringop-overflow=]" "stpncpy" } */
+  return stpncpy (d, ns5, sizeof ns5 + 1);  /* { dg-regexp "\[^\n\r\]+: warning: .stpncpy. argument 2 declared attribute .nonstring. \[^\n\r\]+ \\\[-Wstringop-overread\[^\n\r\]*" "stpncpy" } */
 }
 
 char* strchr_nonstring (NONSTRING const char *s, int c)
 {
-  return strchr (s, c);  /* { dg-regexp "\[^\n\r\]+: warning: .strchr. argument 1 declared attribute .nonstring. \\\[-Wstringop-overflow=]" "strchr" } */
+  return strchr (s, c);  /* { dg-regexp "\[^\n\r\]+: warning: .strchr. argument 1 declared attribute .nonstring. \\\[-Wstringop-overread\[^\n\r\]*" "strchr" } */
 }
 
 char* strrchr_nonstring (NONSTRING const char *s, int c)
 {
-  return strrchr (s, c);  /* { dg-regexp "\[^\n\r\]+: warning: .strrchr. argument 1 declared attribute .nonstring. \\\[-Wstringop-overflow=]" "strrchr" } */
+  return strrchr (s, c);  /* { dg-regexp "\[^\n\r\]+: warning: .strrchr. argument 1 declared attribute .nonstring. \\\[-Wstringop-overread\[^\n\r\]*" "strrchr" } */
 }
 
 char* strcpy_nonstring (char *d, NONSTRING const char *s)
 {
-  return strcpy (d, s);  /* { dg-regexp "\[^\n\r\]+: warning: .strcpy. argument 2 declared attribute .nonstring. \\\[-Wstringop-overflow=]" "strcpy" } */
+  return strcpy (d, s);  /* { dg-regexp "\[^\n\r\]+: warning: .strcpy. argument 2 declared attribute .nonstring. \\\[-Wstringop-overread\[^\n\r\]*" "strcpy" } */
 }
 
 char* strncpy_nonstring (char *d)
 {
-  return strncpy (d, ns5, sizeof ns5 + 1);  /* { dg-regexp "\[^\n\r\]+: warning: .strncpy. argument 2 declared attribute .nonstring. \[^\n\r\]+ \\\[-Wstringop-overflow=]" "strncpy" } */
+  return strncpy (d, ns5, sizeof ns5 + 1);  /* { dg-regexp "\[^\n\r\]+: warning: .strncpy. argument 2 declared attribute .nonstring. \[^\n\r\]+ \\\[-Wstringop-overread\[^\n\r\]*" "strncpy" } */
 }
 
 char* strstr_nonstring_1 (NONSTRING const char *a, const char *b)
 {
-  return strstr (a, b);  /* { dg-regexp "\[^\n\r\]+: warning: .strstr. argument 1 declared attribute .nonstring. \\\[-Wstringop-overflow=]" "strstr" } */
+  return strstr (a, b);  /* { dg-regexp "\[^\n\r\]+: warning: .strstr. argument 1 declared attribute .nonstring. \\\[-Wstringop-overread\[^\n\r\]*" "strstr" } */
 }
 
 char* strstr_nonstring_2 (const char *a, NONSTRING const char *b)
 {
-  return strstr (a, b);  /* { dg-regexp "\[^\n\r\]+: warning: .strstr. argument 2 declared attribute .nonstring. \\\[-Wstringop-overflow=]" "strstr" } */
+  return strstr (a, b);  /* { dg-regexp "\[^\n\r\]+: warning: .strstr. argument 2 declared attribute .nonstring. \\\[-Wstringop-overread\[^\n\r\]*" "strstr" } */
 }
 
 char* stdup_nonstring (NONSTRING const char *s)
 {
-  return strdup (s);  /* { dg-regexp "\[^\n\r\]+: warning: .strdup. argument 1 declared attribute .nonstring. \\\[-Wstringop-overflow=]" "strdup" } */
+  return strdup (s);  /* { dg-regexp "\[^\n\r\]+: warning: .strdup. argument 1 declared attribute .nonstring. \\\[-Wstringop-overread\[^\n\r\]*" "strdup" } */
 }
 
 size_t strlen_nonstring (NONSTRING const char *s)
 {
-  return strlen (s);  /* { dg-regexp "\[^\n\r\]+: warning: .strlen. argument 1 declared attribute .nonstring. \\\[-Wstringop-overflow=]" "strlen" } */
+  return strlen (s);  /* { dg-regexp "\[^\n\r\]+: warning: .strlen. argument 1 declared attribute .nonstring. \\\[-Wstringop-overread\[^\n\r\]*" "strlen" } */
 }
 
 int printf_nonstring (NONSTRING const char *s)
 {
-  return printf (s);  /* { dg-regexp "\[^\n\r\]+: warning: .printf. argument 1 declared attribute .nonstring. \\\[-Wstringop-overflow=]" "printf" } */
+  return printf (s);  /* { dg-regexp "\[^\n\r\]+: warning: .printf. argument 1 declared attribute .nonstring. \\\[-Wstringop-overread\[^\n\r\]*" "printf" } */
 }
 
 int sprintf_nonstring_2 (char *d, NONSTRING const char *s)
 {
-  return sprintf (d, s);  /* { dg-regexp "\[^\n\r\]+: warning: .sprintf. argument 2 declared attribute .nonstring. \\\[-Wstringop-overflow=]" "sprintf" } */
+  return sprintf (d, s);  /* { dg-regexp "\[^\n\r\]+: warning: .sprintf. argument 2 declared attribute .nonstring. \\\[-Wstringop-overread\[^\n\r\]*" "sprintf" } */
 }
diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c
index b07629dad11..7d5b03b9c27 100644
--- a/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c
@@ -87,7 +87,7 @@  void test_memop_warn_local (const void *src)
   memset (&b->d, 0, offsetfrom (struct B, b, d) + 1);   /* { dg-warning "writing 6 bytes into a region of size 5" } */
   escape (b);
 
-  /* Same as above but clearing just elements of the second element
+  /* Same as above but clearing just members of the second element
      of the array.  */
   memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1);   /* { dg-warning "writing 4 bytes into a region of size 3" } */
   escape (b);
diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c
index 12d2491b9f5..f2e9c481172 100644
--- a/gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c
@@ -1,6 +1,6 @@ 
-/* Test exercising -Wstringop-overflow warnings for reading past the end.  */
+/* Test exercising -Wstringop-overread warnings for reading past the end.  */
 /* { dg-do compile } */
-/* { dg-options "-O2 -Wstringop-overflow=1 -ftrack-macro-expansion=0" } */
+/* { dg-options "-O2 -Wstringop-overread -ftrack-macro-expansion=0" } */
 
 #define PTRDIFF_MAX   __PTRDIFF_MAX__
 #define SIZE_MAX      __SIZE_MAX__
@@ -73,22 +73,22 @@  void test_memop_warn_local (void *p, const void *q)
 
   /* Verify memchr/memcmp.  */
   int i = R (0, 255);
-  memchr ("", i, 2);   /* { dg-warning "reading 2 bytes from a region of size 1" } */
-  memchr ("", i, 2);   /* { dg-warning "reading 2 bytes from a region of size 1" } */
-  memchr ("123", i, 5);   /* { dg-warning "reading 5 bytes from a region of size 4" } */
-  memchr (a, i, sizeof a + 1);   /* { dg-warning "reading 5 bytes from a region of size 4" } */
+  memchr ("", i, 2);   /* { dg-warning "specified bound 2 exceeds source size 1" "memchr" } */
+  memchr ("", i, 2);   /* { dg-warning "specified bound 2 exceeds source size 1" "memchr" } */
+  memchr ("123", i, 5);   /* { dg-warning "specified bound 5 exceeds source size 4" "memchr" } */
+  memchr (a, i, sizeof a + 1);   /* { dg-warning "specified bound 5 exceeds source size 4" "memchr" } */
 
-  memcmp (p, "", 2);   /* { dg-warning "reading 2 bytes from a region of size 1" } */
-  memcmp (p, "123", 5);   /* { dg-warning "reading 5 bytes from a region of size 4" } */
-  memcmp (p, a, sizeof a + 1);   /* { dg-warning "reading 5 bytes from a region of size 4" } */
+  memcmp (p, "", 2);   /* { dg-warning "specified bound 2 exceeds source size 1" "memcmp" } */
+  memcmp (p, "123", 5);   /* { dg-warning "specified bound 5 exceeds source size 4" "memcmp" } */
+  memcmp (p, a, sizeof a + 1);   /* { dg-warning "specified bound 5 exceeds source size 4" "memcmp" } */
 
   size_t n = PTRDIFF_MAX + (size_t)1;
-  memchr (p, 1, n);   /* { dg-warning "exceeds maximum object size" } */
-  memcmp (p, q, n);   /* { dg-warning "exceeds maximum object size" } */
+  memchr (p, 1, n);   /* { dg-warning "exceeds maximum object size" "memchr" } */
+  memcmp (p, q, n);   /* { dg-warning "exceeds maximum object size" "memcmp" } */
 
   n = SIZE_MAX;
-  memchr (p, 1, n);   /* { dg-warning "exceeds maximum object size" } */
-  memcmp (p, q, n);   /* { dg-warning "exceeds maximum object size" } */
+  memchr (p, 1, n);   /* { dg-warning "exceeds maximum object size" "memchr" } */
+  memcmp (p, q, n);   /* { dg-warning "exceeds maximum object size" "memcmp" } */
 }
 
 /* Verify that reading beyond the end of a dynamically allocated array
@@ -117,8 +117,8 @@  void test_memop_warn_alloc (void *p)
   /* Verify memchr/memcmp.  */
   n = sizeof *b * 2 + 1;
 
-  memchr (b, 1, n);   /* { dg-warning "reading 9 bytes from a region of size 8" "memcmp from allocated" } */
-  memcmp (p, b, n);   /* { dg-warning "reading 9 bytes from a region of size 8" "memcmp from allocated" } */
+  memchr (b, 1, n);   /* { dg-warning "specified bound 9 exceeds source size 8" "memchr from allocated" } */
+  memcmp (p, b, n);   /* { dg-warning "specified bound 9 exceeds source size 8" "memcmp from allocated" } */
 }
 
 
diff --git a/gcc/testsuite/gcc.dg/pr78902.c b/gcc/testsuite/gcc.dg/pr78902.c
index 51b42548071..a9a617cde23 100644
--- a/gcc/testsuite/gcc.dg/pr78902.c
+++ b/gcc/testsuite/gcc.dg/pr78902.c
@@ -12,4 +12,5 @@  void foo(void)
  __builtin_aligned_alloc (10, 16); /* { dg-warning "ignoring return value of '__builtin_aligned_alloc' declared with attribute 'warn_unused_result'" } */
  __builtin_strdup ("pes"); /* { dg-warning "ignoring return value of '__builtin_strdup' declared with attribute 'warn_unused_result'" } */
  __builtin_strndup ("pes", 10); /* { dg-warning "ignoring return value of '__builtin_strndup' declared with attribute 'warn_unused_result'" } */
+ /* { dg-warning "\\\[-Wstringop-overread" "strndup excessive bound" { target *-*-* } .-1 } */
 }
diff --git a/gcc/testsuite/gcc.dg/pr79214.c b/gcc/testsuite/gcc.dg/pr79214.c
index 23119edf16c..3f5d935d712 100644
--- a/gcc/testsuite/gcc.dg/pr79214.c
+++ b/gcc/testsuite/gcc.dg/pr79214.c
@@ -84,5 +84,5 @@  char* test_strncat (int i)
 {
   const char *s = i < 0 ? "123" : "4567";
 
-  return strncat (d, s, range ());   /* { dg-warning ".__builtin_strncat. specified bound between 4 and \[0-9\]+" } */
+  return strncat (d, s, range ());   /* { dg-warning ".__builtin_strncat. specified bound \\\[4, \[0-9\]+] exceeds destination size 3" } */
 }
diff --git a/gcc/testsuite/gcc.dg/strcmpopt_10.c b/gcc/testsuite/gcc.dg/strcmpopt_10.c
index 94fda596901..d7f94ac4d52 100644
--- a/gcc/testsuite/gcc.dg/strcmpopt_10.c
+++ b/gcc/testsuite/gcc.dg/strcmpopt_10.c
@@ -3,7 +3,7 @@ 
    when the pointer pointed to by the enclosing object references an object
    sufficiently large to store a string of equal length.
   { dg-do compile }
-  { dg-options "-O2 -Wall -Wextra -fdump-tree-optimized" } */
+  { dg-options "-O2 -Wall -Wextra -Wno-stringop-overread -fdump-tree-optimized" } */
 
 void init (void*);
 
diff --git a/gcc/testsuite/gcc.dg/strlenopt-57.c b/gcc/testsuite/gcc.dg/strlenopt-57.c
index b7212bcf795..2f67babec88 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-57.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-57.c
@@ -21,9 +21,9 @@  void test_var_flexarray_cst_off (void)
 {
   /* Use arbitrary constants greater than 16 in case GCC ever starts
      unrolling strlen() calls with small array arguments.  */
-  a[0] = 17 < strlen (a0.a + 1);        // { dg-warning "\\\[-Warray-bounds" }
-  a[1] = 19 < strlen (a1.a + 1);
-  a[2] = 23 < strlen (a9.a + 9);
+  a[0] = 17 < strlen (a0.a + 1);        // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
+  a[1] = 19 < strlen (a1.a + 1);        // { dg-warning "\\\[-Wstringop-overread" }
+  a[2] = 23 < strlen (a9.a + 9);        // { dg-warning "\\\[-Wstringop-overread" }
   a[3] = 29 < strlen (ax.a + 3);
 }
 
diff --git a/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
index 11367d1ec7a..12e6f255bf5 100644
--- a/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
@@ -1,6 +1,6 @@ 
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-overread -Wno-stringop-truncation" } */
 /* Test just twice, once with -O0 non-fortified, once with -O2 fortified.  */
 /* { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } } */
 /* { dg-skip-if "" { *-*-* }  { "-flto" } { "" } } */
diff --git a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul-2.c b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul-2.c
index e91e8cef751..76180a3f5da 100644
--- a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul-2.c
+++ b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul-2.c
@@ -62,5 +62,11 @@  void test (int n0)
 
   int n = n0 < n1 ? n1 : n0;
 
-  sink (strnlen (c + n, n + 1));    /* { dg-warning "specified bound \\\[5, \[0-9\]+] may exceed the size of at most 4 of unterminated array" } */
+  /* N is at least 4 and c[4] is out-of-bounds.  This could trigger
+     either -Warray-bounds or -Wstringop-overread.  -Warray-bounds
+     only diagnoses past-the-end accesses by modifying functions
+     (in gimple-ssa-warn-restrict.c) and even for those, either
+     -Wstringop-overflow or -Wstringop-overread would be more
+     appropriate.  */
+  sink (strnlen (c + n, n + 1));    /* { dg-warning "specified bound \\\[5, \[0-9\]+] exceeds the size of at most 4 of unterminated array" } */
 }
diff --git a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
index 09a527ea337..02f6f3d5342 100644
--- a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
+++ b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
@@ -35,7 +35,7 @@  T (&a[1], asz - 1);
 T (&a[v0], asz);               /* { dg-warning "specified bound 5 may exceed the size of at most 5 of unterminated array" } */
 T (&a[v0] + 1, asz);           /* { dg-warning "specified bound 5 may exceed the size of at most 5 of unterminated array" } */
 
-T (a, asz + 1);                /* { dg-warning "specified bound 6 exceeds the size 5 " } */
+T (a, asz + 1);                /* { dg-warning "specified bound 6 exceeds the size 5 of unterminated array" } */
 T (&a[0], asz + 1);            /* { dg-warning "unterminated" } */
 T (&a[0] + 1, asz - 1);
 T (&a[0] + 1, asz + 1);        /* { dg-warning "unterminated" } */
@@ -110,7 +110,7 @@  T (&b[3][1] + i1, bsz);           /* { dg-warning "unterminated" } */
 T (&b[3][1] + i1, bsz - i1);      /* { dg-warning "unterminated" } */
 T (&b[3][1] + i1, bsz - i2);
 T (&b[3][v0], bsz);
-T (&b[3][1] + v0, bsz);           /* { dg-warning "specified bound 5 may exceed the size of at most 4 of unterminated array" } */
+T (&b[3][1] + v0, bsz);           /* { dg-warning "specified bound 5 exceeds the size of at most 4 of unterminated array" } */
 T (&b[3][v0] + v1, bsz);          /* { dg-warning "specified bound 5 may exceed the size of at most 4 of unterminated array" "pr?????" { xfail *-*-* } } */
 
 T (&b[3][1], bsz + 1);            /* { dg-warning "unterminated" } */
@@ -124,7 +124,7 @@  T (&b[i3][i1], bsz);              /* { dg-warning "unterminated" } */
 T (&b[i3][i1] + 1, bsz);          /* { dg-warning "unterminated" } */
 T (&b[i3][i1] + i1, bsz);         /* { dg-warning "specified bound 5 exceeds the size 3 of unterminated array" } */
 T (&b[i3][v0], bsz);
-T (&b[i3][i1] + v0, bsz);         /* { dg-warning "specified bound 5 may exceed the size of at most 4 of unterminated array" } */
+T (&b[i3][i1] + v0, bsz);         /* { dg-warning "specified bound 5 exceeds the size of at most 4 of unterminated array" } */
 T (&b[i3][v0] + v1, bsz);
 
 T (&b[i3][i1], bsz + 1);          /* { dg-warning "unterminated" } */
@@ -212,10 +212,10 @@  T (&s.a[i1] + v0, asz);
 T (s.a, asz + 1);
 T (&s.a[0], asz + 1);
 T (&s.a[0] + 1, asz + 1);
-T (&s.a[0] + v0, asz + 1);
+T (&s.a[0] + v0, asz + 1);        /* { dg-warning "specified bound 6 exceeds source size 5 " } */
 T (&s.a[1], asz + 1);
 T (&s.a[1] + 1, asz + 1);
-T (&s.a[1] + v0, asz + 1);
+T (&s.a[1] + v0, asz + 1);        /* { dg-bogus "specified bound 6 exceeds source size 5" "pr95794" { xfail *-*-* } } */
 
 T (&s.a[i0], asz + 1);
 T (&s.a[i0] + i1, asz + 1);
@@ -266,10 +266,10 @@  const struct B ba[] = {
 T (ba[0].a[0].a, asz + 1);
 T (&ba[0].a[0].a[0], asz + 1);
 T (&ba[0].a[0].a[0] + 1, asz + 1);
-T (&ba[0].a[0].a[0] + v0, asz + 1);
+T (&ba[0].a[0].a[0] + v0, asz + 1);   /* { dg-bogus "specified bound 6 exceeds source size 5" pr95794" { xfail *-*-* } } */
 T (&ba[0].a[0].a[1], asz + 1);
 T (&ba[0].a[0].a[1] + 1, asz + 1);
-T (&ba[0].a[0].a[1] + v0, asz + 1);
+T (&ba[0].a[0].a[1] + v0, asz + 1);   /* { dg-bogus "specified bound 6 exceeds source size 5" pr95794" { xfail *-*-* } } */
 
 T (ba[0].a[0].b, bsz);
 T (&ba[0].a[0].b[0], bsz);
@@ -302,10 +302,10 @@  T (&ba[0].a[1].a[1] + v0, asz + 1);   /* { dg-warning "unterminated" } */
 T (ba[0].a[1].b, bsz + 1);
 T (&ba[0].a[1].b[0], bsz + 1);
 T (&ba[0].a[1].b[0] + 1, bsz + 1);
-T (&ba[0].a[1].b[0] + v0, bsz + 1);
+T (&ba[0].a[1].b[0] + v0, bsz + 1);   /* { dg-bogus "specified bound 6 exceeds source size 5" pr95794" { xfail *-*-* } } */
 T (&ba[0].a[1].b[1], bsz + 1);
 T (&ba[0].a[1].b[1] + 1, bsz + 1);
-T (&ba[0].a[1].b[1] + v0, bsz + 1);
+T (&ba[0].a[1].b[1] + v0, bsz + 1);   /* { dg-bogus "specified bound 6 exceeds source size 5" pr95794" { xfail *-*-* } } */
 
 T (ba[1].a[0].a, asz);
 T (&ba[1].a[0].a[0], asz);
diff --git a/gcc/tree-ssa-uninit.c b/gcc/tree-ssa-uninit.c
index 2f0ff724cde..7eddca397b3 100644
--- a/gcc/tree-ssa-uninit.c
+++ b/gcc/tree-ssa-uninit.c
@@ -489,10 +489,10 @@  maybe_warn_pass_by_reference (gimple *stmt, wlimits &wlims)
       attr_access *access = rdwr_idx.get (argno - 1);
       if (access)
 	{
-	  if (access->mode == attr_access::none
-	      || access->mode == attr_access::write_only)
+	  if (access->mode == access_none
+	      || access->mode == access_write_only)
 	    continue;
-	  if (save_always_executed && access->mode == attr_access::read_only)
+	  if (save_always_executed && access->mode == access_read_only)
 	    /* Attribute read_only arguments imply read access.  */
 	    wlims.always_executed = true;
 	  else
@@ -524,7 +524,7 @@  maybe_warn_pass_by_reference (gimple *stmt, wlimits &wlims)
 
       if (access)
 	{
-	  const char* const mode = (access->mode == attr_access::read_only
+	  const char* const mode = (access->mode == access_read_only
 				    ? "read_only" : "read_write");
 	  char attrstr[80];
 	  int n = sprintf (attrstr, "access (%s, %u", mode, argno);
diff --git a/gcc/tree.h b/gcc/tree.h
index a74872f5f3e..882efda1e11 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5345,6 +5345,15 @@  canonical_type_used_p (const_tree t)
 	   || TREE_CODE (t) == VECTOR_TYPE);
 }
 
+/* Kinds of access to pass-by-reference arguments to functions.  */
+enum access_mode
+{
+  access_none = 0,
+  access_read_only = 1,
+  access_write_only = 2,
+  access_read_write = access_read_only | access_write_only
+};
+
 #define tree_map_eq tree_map_base_eq
 extern unsigned int tree_map_hash (const void *);
 #define tree_map_marked_p tree_map_base_marked_p