diff mbox series

Make full use of context-sensitive ranges in access warnings

Message ID 1648e958-ece9-dca8-4d47-cded635d48c3@gmail.com
State New
Headers show
Series Make full use of context-sensitive ranges in access warnings | expand

Commit Message

Martin Sebor Oct. 23, 2021, 11:49 p.m. UTC
Somewhat belatedly following Aldy's lead on finishing
the conversion to Ranger, the attached patch modifies
gimple-ssa-warn-access and other passes that use
the pointer_query machinery to provide Ranger with
the statement it's being called to determine ranges for.
The changes are almost completely mechanical, involving
passing a GIMPLE statement around (and a range_query
pointer) all the way into the bowels of the pointer_query
class to make them available when range info is being
determined.

There might be some overlap with Aldy's tree-ssa-strlen.c
changes to do the same there.  I'll deal with any conflicts
when it comes time to commit the work.

The changes trigger a couple of -Wstringop-overread instances
in libstdc++ tests.  The warnings look valid for the IL but
the code they're in is unreachable.  One of the tests already
suppresses -Wstringop-overflow so also suppressing
-Wstringop-overread doesn't seem out of line.

Tested on x86_64-linux.

Martin

PS The warning for the u8path-char8_t.cc test is this:

/ssd/test/build/gcc-test/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/char_traits.h:355: 
warning: 'void* __builtin_memcpy(void*, const void*, long unsigned int)' 
reading between 16 and 4611686018427387903 bytes from a region of size 
10 [-Wstringop-overread]

The IL for it is below.  The loop iN BB 3 exits with __i_22 equal
to 10 so BBs 5, 6 and 7 are unreachable.  It's surprising to me
that the loop isn't optimized into something better (like a MEM
array assignment or memcpy).

   <bb 2> [local count: 1073741824]:
   MEM[(struct basic_string *)&s1] ={v} {CLOBBER};
   MEM[(struct _Alloc_hider *)&s1] ={v} {CLOBBER};
   MEM[(struct _Alloc_hider *)&s1]._M_p = &s1.D.30357._M_local_buf;

   <bb 3> [local count: 8687547547]:
   # __i_109 = PHI <__i_22(3), 0(2)>
   __i_22 = __i_109 + 1;
   _24 = MEM[(const char_type &)"filename2" + __i_22 * 1];
   if (_24 != 0)
     goto <bb 3>; [89.00%]
   else
     goto <bb 4>; [11.00%]

   <bb 4> [local count: 1073741824]:   <<< __i_22 == 10 here
   if (__i_22 > 15)
     goto <bb 5>; [33.00%]
   else
     goto <bb 8>; [67.00%]

   <bb 5> [local count: 354334802]:
   if (__i_22 > 4611686018427387903)
     goto <bb 6>; [0.04%]
   else
     goto <bb 7>; [99.96%]   >>> __i_22 in [16, 4611686018427387903]

   <bb 6> [local count: 141736]:
   std::__throw_length_error ("basic_string::_M_create");

   <bb 7> [local count: 354193066]:
   _85 = __i_109 + 2;
   _42 = operator new (_85);
   s1._M_dataplus._M_p = _42;
   s1.D.30357._M_allocated_capacity = __i_22;
   __builtin_memcpy (_42, "filename2", __i_22);   << -Wstringop-overread

Comments

Jeff Law Oct. 25, 2021, 6:57 p.m. UTC | #1
On 10/23/2021 5:49 PM, Martin Sebor via Gcc-patches wrote:
> Somewhat belatedly following Aldy's lead on finishing
> the conversion to Ranger, the attached patch modifies
> gimple-ssa-warn-access and other passes that use
> the pointer_query machinery to provide Ranger with
> the statement it's being called to determine ranges for.
> The changes are almost completely mechanical, involving
> passing a GIMPLE statement around (and a range_query
> pointer) all the way into the bowels of the pointer_query
> class to make them available when range info is being
> determined.
>
> There might be some overlap with Aldy's tree-ssa-strlen.c
> changes to do the same there.  I'll deal with any conflicts
> when it comes time to commit the work.
>
> The changes trigger a couple of -Wstringop-overread instances
> in libstdc++ tests.  The warnings look valid for the IL but
> the code they're in is unreachable.  One of the tests already
> suppresses -Wstringop-overflow so also suppressing
> -Wstringop-overread doesn't seem out of line.
>
> Tested on x86_64-linux.
>
> Martin
>
> PS The warning for the u8path-char8_t.cc test is this:
>
> /ssd/test/build/gcc-test/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/char_traits.h:355: 
> warning: 'void* __builtin_memcpy(void*, const void*, long unsigned 
> int)' reading between 16 and 4611686018427387903 bytes from a region 
> of size 10 [-Wstringop-overread]
>
> The IL for it is below.  The loop iN BB 3 exits with __i_22 equal
> to 10 so BBs 5, 6 and 7 are unreachable.  It's surprising to me
> that the loop isn't optimized into something better (like a MEM
> array assignment or memcpy).
>
>   <bb 2> [local count: 1073741824]:
>   MEM[(struct basic_string *)&s1] ={v} {CLOBBER};
>   MEM[(struct _Alloc_hider *)&s1] ={v} {CLOBBER};
>   MEM[(struct _Alloc_hider *)&s1]._M_p = &s1.D.30357._M_local_buf;
>
>   <bb 3> [local count: 8687547547]:
>   # __i_109 = PHI <__i_22(3), 0(2)>
>   __i_22 = __i_109 + 1;
>   _24 = MEM[(const char_type &)"filename2" + __i_22 * 1];
>   if (_24 != 0)
>     goto <bb 3>; [89.00%]
>   else
>     goto <bb 4>; [11.00%]
>
>   <bb 4> [local count: 1073741824]:   <<< __i_22 == 10 here
>   if (__i_22 > 15)
>     goto <bb 5>; [33.00%]
>   else
>     goto <bb 8>; [67.00%]
>
>   <bb 5> [local count: 354334802]:
>   if (__i_22 > 4611686018427387903)
>     goto <bb 6>; [0.04%]
>   else
>     goto <bb 7>; [99.96%]   >>> __i_22 in [16, 4611686018427387903]
>
>   <bb 6> [local count: 141736]:
>   std::__throw_length_error ("basic_string::_M_create");
>
>   <bb 7> [local count: 354193066]:
>   _85 = __i_109 + 2;
>   _42 = operator new (_85);
>   s1._M_dataplus._M_p = _42;
>   s1.D.30357._M_allocated_capacity = __i_22;
>   __builtin_memcpy (_42, "filename2", __i_22);   << -Wstringop-overread

Do you mean __i_22 == 16 earlier?  I don't see how it's restricted to 10.

I would have expected to have a global range for i_22 of [0,16] which in 
turn should have allowed the optimizers to remove bb5 and bb6.  Not sure 
if that'd fix your overread though.

OK.  I'll let you and Aldy coordinate since y'all may be hitting some of 
the same bits.

jeff
Martin Sebor Oct. 25, 2021, 7:31 p.m. UTC | #2
On 10/25/21 12:57 PM, Jeff Law wrote:
> 
> 
> On 10/23/2021 5:49 PM, Martin Sebor via Gcc-patches wrote:
>> Somewhat belatedly following Aldy's lead on finishing
>> the conversion to Ranger, the attached patch modifies
>> gimple-ssa-warn-access and other passes that use
>> the pointer_query machinery to provide Ranger with
>> the statement it's being called to determine ranges for.
>> The changes are almost completely mechanical, involving
>> passing a GIMPLE statement around (and a range_query
>> pointer) all the way into the bowels of the pointer_query
>> class to make them available when range info is being
>> determined.
>>
>> There might be some overlap with Aldy's tree-ssa-strlen.c
>> changes to do the same there.  I'll deal with any conflicts
>> when it comes time to commit the work.
>>
>> The changes trigger a couple of -Wstringop-overread instances
>> in libstdc++ tests.  The warnings look valid for the IL but
>> the code they're in is unreachable.  One of the tests already
>> suppresses -Wstringop-overflow so also suppressing
>> -Wstringop-overread doesn't seem out of line.
>>
>> Tested on x86_64-linux.
>>
>> Martin
>>
>> PS The warning for the u8path-char8_t.cc test is this:
>>
>> /ssd/test/build/gcc-test/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/char_traits.h:355: 
>> warning: 'void* __builtin_memcpy(void*, const void*, long unsigned 
>> int)' reading between 16 and 4611686018427387903 bytes from a region 
>> of size 10 [-Wstringop-overread]
>>
>> The IL for it is below.  The loop iN BB 3 exits with __i_22 equal
>> to 10 so BBs 5, 6 and 7 are unreachable.  It's surprising to me
>> that the loop isn't optimized into something better (like a MEM
>> array assignment or memcpy).
>>
>>   <bb 2> [local count: 1073741824]:
>>   MEM[(struct basic_string *)&s1] ={v} {CLOBBER};
>>   MEM[(struct _Alloc_hider *)&s1] ={v} {CLOBBER};
>>   MEM[(struct _Alloc_hider *)&s1]._M_p = &s1.D.30357._M_local_buf;
>>
>>   <bb 3> [local count: 8687547547]:
>>   # __i_109 = PHI <__i_22(3), 0(2)>
>>   __i_22 = __i_109 + 1;
>>   _24 = MEM[(const char_type &)"filename2" + __i_22 * 1];
>>   if (_24 != 0)
>>     goto <bb 3>; [89.00%]
>>   else
>>     goto <bb 4>; [11.00%]
>>
>>   <bb 4> [local count: 1073741824]:   <<< __i_22 == 10 here
>>   if (__i_22 > 15)
>>     goto <bb 5>; [33.00%]
>>   else
>>     goto <bb 8>; [67.00%]
>>
>>   <bb 5> [local count: 354334802]:
>>   if (__i_22 > 4611686018427387903)
>>     goto <bb 6>; [0.04%]
>>   else
>>     goto <bb 7>; [99.96%]   >>> __i_22 in [16, 4611686018427387903]
>>
>>   <bb 6> [local count: 141736]:
>>   std::__throw_length_error ("basic_string::_M_create");
>>
>>   <bb 7> [local count: 354193066]:
>>   _85 = __i_109 + 2;
>>   _42 = operator new (_85);
>>   s1._M_dataplus._M_p = _42;
>>   s1.D.30357._M_allocated_capacity = __i_22;
>>   __builtin_memcpy (_42, "filename2", __i_22);   << -Wstringop-overread
> 
> Do you mean __i_22 == 16 earlier?  I don't see how it's restricted to 10.

The loop computes the size of the "filename2" string so the result
is 10, no?

> 
> I would have expected to have a global range for i_22 of [0,16] which in 
> turn should have allowed the optimizers to remove bb5 and bb6.  Not sure 
> if that'd fix your overread though.
> 
> OK.  I'll let you and Aldy coordinate since y'all may be hitting some of 
> the same bits.

Will do.

Martin
Martin Sebor Oct. 25, 2021, 7:31 p.m. UTC | #3
On 10/25/21 12:57 PM, Jeff Law wrote:
> 
> 
> On 10/23/2021 5:49 PM, Martin Sebor via Gcc-patches wrote:
>> Somewhat belatedly following Aldy's lead on finishing
>> the conversion to Ranger, the attached patch modifies
>> gimple-ssa-warn-access and other passes that use
>> the pointer_query machinery to provide Ranger with
>> the statement it's being called to determine ranges for.
>> The changes are almost completely mechanical, involving
>> passing a GIMPLE statement around (and a range_query
>> pointer) all the way into the bowels of the pointer_query
>> class to make them available when range info is being
>> determined.
>>
>> There might be some overlap with Aldy's tree-ssa-strlen.c
>> changes to do the same there.  I'll deal with any conflicts
>> when it comes time to commit the work.
>>
>> The changes trigger a couple of -Wstringop-overread instances
>> in libstdc++ tests.  The warnings look valid for the IL but
>> the code they're in is unreachable.  One of the tests already
>> suppresses -Wstringop-overflow so also suppressing
>> -Wstringop-overread doesn't seem out of line.
>>
>> Tested on x86_64-linux.
>>
>> Martin
>>
>> PS The warning for the u8path-char8_t.cc test is this:
>>
>> /ssd/test/build/gcc-test/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/char_traits.h:355: 
>> warning: 'void* __builtin_memcpy(void*, const void*, long unsigned 
>> int)' reading between 16 and 4611686018427387903 bytes from a region 
>> of size 10 [-Wstringop-overread]
>>
>> The IL for it is below.  The loop iN BB 3 exits with __i_22 equal
>> to 10 so BBs 5, 6 and 7 are unreachable.  It's surprising to me
>> that the loop isn't optimized into something better (like a MEM
>> array assignment or memcpy).
>>
>>   <bb 2> [local count: 1073741824]:
>>   MEM[(struct basic_string *)&s1] ={v} {CLOBBER};
>>   MEM[(struct _Alloc_hider *)&s1] ={v} {CLOBBER};
>>   MEM[(struct _Alloc_hider *)&s1]._M_p = &s1.D.30357._M_local_buf;
>>
>>   <bb 3> [local count: 8687547547]:
>>   # __i_109 = PHI <__i_22(3), 0(2)>
>>   __i_22 = __i_109 + 1;
>>   _24 = MEM[(const char_type &)"filename2" + __i_22 * 1];
>>   if (_24 != 0)
>>     goto <bb 3>; [89.00%]
>>   else
>>     goto <bb 4>; [11.00%]
>>
>>   <bb 4> [local count: 1073741824]:   <<< __i_22 == 10 here
>>   if (__i_22 > 15)
>>     goto <bb 5>; [33.00%]
>>   else
>>     goto <bb 8>; [67.00%]
>>
>>   <bb 5> [local count: 354334802]:
>>   if (__i_22 > 4611686018427387903)
>>     goto <bb 6>; [0.04%]
>>   else
>>     goto <bb 7>; [99.96%]   >>> __i_22 in [16, 4611686018427387903]
>>
>>   <bb 6> [local count: 141736]:
>>   std::__throw_length_error ("basic_string::_M_create");
>>
>>   <bb 7> [local count: 354193066]:
>>   _85 = __i_109 + 2;
>>   _42 = operator new (_85);
>>   s1._M_dataplus._M_p = _42;
>>   s1.D.30357._M_allocated_capacity = __i_22;
>>   __builtin_memcpy (_42, "filename2", __i_22);   << -Wstringop-overread
> 
> Do you mean __i_22 == 16 earlier?  I don't see how it's restricted to 10.

The loop computes the size of the "filename2" string so the result
is 10, no?

> 
> I would have expected to have a global range for i_22 of [0,16] which in 
> turn should have allowed the optimizers to remove bb5 and bb6.  Not sure 
> if that'd fix your overread though.
> 
> OK.  I'll let you and Aldy coordinate since y'all may be hitting some of 
> the same bits.

Will do.

Martin
Jeff Law Oct. 25, 2021, 8:24 p.m. UTC | #4
On 10/25/2021 1:31 PM, Martin Sebor wrote:
> On 10/25/21 12:57 PM, Jeff Law wrote:
>>
>>
>> On 10/23/2021 5:49 PM, Martin Sebor via Gcc-patches wrote:
>>> Somewhat belatedly following Aldy's lead on finishing
>>> the conversion to Ranger, the attached patch modifies
>>> gimple-ssa-warn-access and other passes that use
>>> the pointer_query machinery to provide Ranger with
>>> the statement it's being called to determine ranges for.
>>> The changes are almost completely mechanical, involving
>>> passing a GIMPLE statement around (and a range_query
>>> pointer) all the way into the bowels of the pointer_query
>>> class to make them available when range info is being
>>> determined.
>>>
>>> There might be some overlap with Aldy's tree-ssa-strlen.c
>>> changes to do the same there.  I'll deal with any conflicts
>>> when it comes time to commit the work.
>>>
>>> The changes trigger a couple of -Wstringop-overread instances
>>> in libstdc++ tests.  The warnings look valid for the IL but
>>> the code they're in is unreachable.  One of the tests already
>>> suppresses -Wstringop-overflow so also suppressing
>>> -Wstringop-overread doesn't seem out of line.
>>>
>>> Tested on x86_64-linux.
>>>
>>> Martin
>>>
>>> PS The warning for the u8path-char8_t.cc test is this:
>>>
>>> /ssd/test/build/gcc-test/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/char_traits.h:355: 
>>> warning: 'void* __builtin_memcpy(void*, const void*, long unsigned 
>>> int)' reading between 16 and 4611686018427387903 bytes from a region 
>>> of size 10 [-Wstringop-overread]
>>>
>>> The IL for it is below.  The loop iN BB 3 exits with __i_22 equal
>>> to 10 so BBs 5, 6 and 7 are unreachable.  It's surprising to me
>>> that the loop isn't optimized into something better (like a MEM
>>> array assignment or memcpy).
>>>
>>>   <bb 2> [local count: 1073741824]:
>>>   MEM[(struct basic_string *)&s1] ={v} {CLOBBER};
>>>   MEM[(struct _Alloc_hider *)&s1] ={v} {CLOBBER};
>>>   MEM[(struct _Alloc_hider *)&s1]._M_p = &s1.D.30357._M_local_buf;
>>>
>>>   <bb 3> [local count: 8687547547]:
>>>   # __i_109 = PHI <__i_22(3), 0(2)>
>>>   __i_22 = __i_109 + 1;
>>>   _24 = MEM[(const char_type &)"filename2" + __i_22 * 1];
>>>   if (_24 != 0)
>>>     goto <bb 3>; [89.00%]
>>>   else
>>>     goto <bb 4>; [11.00%]
>>>
>>>   <bb 4> [local count: 1073741824]:   <<< __i_22 == 10 here
>>>   if (__i_22 > 15)
>>>     goto <bb 5>; [33.00%]
>>>   else
>>>     goto <bb 8>; [67.00%]
>>>
>>>   <bb 5> [local count: 354334802]:
>>>   if (__i_22 > 4611686018427387903)
>>>     goto <bb 6>; [0.04%]
>>>   else
>>>     goto <bb 7>; [99.96%]   >>> __i_22 in [16, 4611686018427387903]
>>>
>>>   <bb 6> [local count: 141736]:
>>>   std::__throw_length_error ("basic_string::_M_create");
>>>
>>>   <bb 7> [local count: 354193066]:
>>>   _85 = __i_109 + 2;
>>>   _42 = operator new (_85);
>>>   s1._M_dataplus._M_p = _42;
>>>   s1.D.30357._M_allocated_capacity = __i_22;
>>>   __builtin_memcpy (_42, "filename2", __i_22);   << -Wstringop-overread
>>
>> Do you mean __i_22 == 16 earlier?  I don't see how it's restricted to 
>> 10.
>
> The loop computes the size of the "filename2" string so the result
> is 10, no?
Oh, duh.  I'm not sure that Ranger will pick that up though.

jeff
Andrew MacLeod Oct. 25, 2021, 8:55 p.m. UTC | #5
On 10/25/21 4:24 PM, Jeff Law via Gcc-patches wrote:
>
>
> On 10/25/2021 1:31 PM, Martin Sebor wrote:
>> On 10/25/21 12:57 PM, Jeff Law wrote:
>>>
>>>
>>> On 10/23/2021 5:49 PM, Martin Sebor via Gcc-patches wrote:
>>>> Somewhat belatedly following Aldy's lead on finishing
>>>> the conversion to Ranger, the attached patch modifies
>>>> gimple-ssa-warn-access and other passes that use
>>>> the pointer_query machinery to provide Ranger with
>>>> the statement it's being called to determine ranges for.
>>>> The changes are almost completely mechanical, involving
>>>> passing a GIMPLE statement around (and a range_query
>>>> pointer) all the way into the bowels of the pointer_query
>>>> class to make them available when range info is being
>>>> determined.
>>>>
>>>> There might be some overlap with Aldy's tree-ssa-strlen.c
>>>> changes to do the same there.  I'll deal with any conflicts
>>>> when it comes time to commit the work.
>>>>
>>>> The changes trigger a couple of -Wstringop-overread instances
>>>> in libstdc++ tests.  The warnings look valid for the IL but
>>>> the code they're in is unreachable.  One of the tests already
>>>> suppresses -Wstringop-overflow so also suppressing
>>>> -Wstringop-overread doesn't seem out of line.
>>>>
>>>> Tested on x86_64-linux.
>>>>
>>>> Martin
>>>>
>>>> PS The warning for the u8path-char8_t.cc test is this:
>>>>
>>>> /ssd/test/build/gcc-test/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/char_traits.h:355: 
>>>> warning: 'void* __builtin_memcpy(void*, const void*, long unsigned 
>>>> int)' reading between 16 and 4611686018427387903 bytes from a 
>>>> region of size 10 [-Wstringop-overread]
>>>>
>>>> The IL for it is below.  The loop iN BB 3 exits with __i_22 equal
>>>> to 10 so BBs 5, 6 and 7 are unreachable.  It's surprising to me
>>>> that the loop isn't optimized into something better (like a MEM
>>>> array assignment or memcpy).
>>>>
>>>>   <bb 2> [local count: 1073741824]:
>>>>   MEM[(struct basic_string *)&s1] ={v} {CLOBBER};
>>>>   MEM[(struct _Alloc_hider *)&s1] ={v} {CLOBBER};
>>>>   MEM[(struct _Alloc_hider *)&s1]._M_p = &s1.D.30357._M_local_buf;
>>>>
>>>>   <bb 3> [local count: 8687547547]:
>>>>   # __i_109 = PHI <__i_22(3), 0(2)>
>>>>   __i_22 = __i_109 + 1;
>>>>   _24 = MEM[(const char_type &)"filename2" + __i_22 * 1];
>>>>   if (_24 != 0)
>>>>     goto <bb 3>; [89.00%]
>>>>   else
>>>>     goto <bb 4>; [11.00%]
>>>>
>>>>   <bb 4> [local count: 1073741824]:   <<< __i_22 == 10 here
>>>>   if (__i_22 > 15)
>>>>     goto <bb 5>; [33.00%]
>>>>   else
>>>>     goto <bb 8>; [67.00%]
>>>>
>>>>   <bb 5> [local count: 354334802]:
>>>>   if (__i_22 > 4611686018427387903)
>>>>     goto <bb 6>; [0.04%]
>>>>   else
>>>>     goto <bb 7>; [99.96%]   >>> __i_22 in [16, 4611686018427387903]
>>>>
>>>>   <bb 6> [local count: 141736]:
>>>>   std::__throw_length_error ("basic_string::_M_create");
>>>>
>>>>   <bb 7> [local count: 354193066]:
>>>>   _85 = __i_109 + 2;
>>>>   _42 = operator new (_85);
>>>>   s1._M_dataplus._M_p = _42;
>>>>   s1.D.30357._M_allocated_capacity = __i_22;
>>>>   __builtin_memcpy (_42, "filename2", __i_22);   << 
>>>> -Wstringop-overread
>>>
>>> Do you mean __i_22 == 16 earlier?  I don't see how it's restricted 
>>> to 10.
>>
>> The loop computes the size of the "filename2" string so the result
>> is 10, no?
> Oh, duh.  I'm not sure that Ranger will pick that up though.
Absolutely not on its own, unless its globally set earlier by loop 
analysis or someone else.
Martin Sebor Oct. 25, 2021, 9:04 p.m. UTC | #6
On 10/25/21 2:24 PM, Jeff Law wrote:
> 
> 
> On 10/25/2021 1:31 PM, Martin Sebor wrote:
>> On 10/25/21 12:57 PM, Jeff Law wrote:
>>>
>>>
>>> On 10/23/2021 5:49 PM, Martin Sebor via Gcc-patches wrote:
>>>> Somewhat belatedly following Aldy's lead on finishing
>>>> the conversion to Ranger, the attached patch modifies
>>>> gimple-ssa-warn-access and other passes that use
>>>> the pointer_query machinery to provide Ranger with
>>>> the statement it's being called to determine ranges for.
>>>> The changes are almost completely mechanical, involving
>>>> passing a GIMPLE statement around (and a range_query
>>>> pointer) all the way into the bowels of the pointer_query
>>>> class to make them available when range info is being
>>>> determined.
>>>>
>>>> There might be some overlap with Aldy's tree-ssa-strlen.c
>>>> changes to do the same there.  I'll deal with any conflicts
>>>> when it comes time to commit the work.
>>>>
>>>> The changes trigger a couple of -Wstringop-overread instances
>>>> in libstdc++ tests.  The warnings look valid for the IL but
>>>> the code they're in is unreachable.  One of the tests already
>>>> suppresses -Wstringop-overflow so also suppressing
>>>> -Wstringop-overread doesn't seem out of line.
>>>>
>>>> Tested on x86_64-linux.
>>>>
>>>> Martin
>>>>
>>>> PS The warning for the u8path-char8_t.cc test is this:
>>>>
>>>> /ssd/test/build/gcc-test/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/char_traits.h:355: 
>>>> warning: 'void* __builtin_memcpy(void*, const void*, long unsigned 
>>>> int)' reading between 16 and 4611686018427387903 bytes from a region 
>>>> of size 10 [-Wstringop-overread]
>>>>
>>>> The IL for it is below.  The loop iN BB 3 exits with __i_22 equal
>>>> to 10 so BBs 5, 6 and 7 are unreachable.  It's surprising to me
>>>> that the loop isn't optimized into something better (like a MEM
>>>> array assignment or memcpy).
>>>>
>>>>   <bb 2> [local count: 1073741824]:
>>>>   MEM[(struct basic_string *)&s1] ={v} {CLOBBER};
>>>>   MEM[(struct _Alloc_hider *)&s1] ={v} {CLOBBER};
>>>>   MEM[(struct _Alloc_hider *)&s1]._M_p = &s1.D.30357._M_local_buf;
>>>>
>>>>   <bb 3> [local count: 8687547547]:
>>>>   # __i_109 = PHI <__i_22(3), 0(2)>
>>>>   __i_22 = __i_109 + 1;
>>>>   _24 = MEM[(const char_type &)"filename2" + __i_22 * 1];
>>>>   if (_24 != 0)
>>>>     goto <bb 3>; [89.00%]
>>>>   else
>>>>     goto <bb 4>; [11.00%]
>>>>
>>>>   <bb 4> [local count: 1073741824]:   <<< __i_22 == 10 here
>>>>   if (__i_22 > 15)
>>>>     goto <bb 5>; [33.00%]
>>>>   else
>>>>     goto <bb 8>; [67.00%]
>>>>
>>>>   <bb 5> [local count: 354334802]:
>>>>   if (__i_22 > 4611686018427387903)
>>>>     goto <bb 6>; [0.04%]
>>>>   else
>>>>     goto <bb 7>; [99.96%]   >>> __i_22 in [16, 4611686018427387903]
>>>>
>>>>   <bb 6> [local count: 141736]:
>>>>   std::__throw_length_error ("basic_string::_M_create");
>>>>
>>>>   <bb 7> [local count: 354193066]:
>>>>   _85 = __i_109 + 2;
>>>>   _42 = operator new (_85);
>>>>   s1._M_dataplus._M_p = _42;
>>>>   s1.D.30357._M_allocated_capacity = __i_22;
>>>>   __builtin_memcpy (_42, "filename2", __i_22);   << -Wstringop-overread
>>>
>>> Do you mean __i_22 == 16 earlier?  I don't see how it's restricted to 
>>> 10.
>>
>> The loop computes the size of the "filename2" string so the result
>> is 10, no?
> Oh, duh.  I'm not sure that Ranger will pick that up though.

I don't expect Ranger to figure it out from the loop, but I'd
expect the loop to be unrolled into a constant.  It's just:

   int __i_22 = 0;
   do; while ("filename2"[__i22++]);

and this and all its variations I've tried is folded to 10.
Something in the bowels of std::u8string is keeping that from
happening.

The test case that shows the difference is just:

#include <string>

int f ()
{
   std::string s = "filename2";
   return s.length ();   // folded to 10
}

int g ()
{
   std::u8string s = u8"filename2";
   return s.length ();   // not folded
}

Compile with -std=c++17 -fchar8_t.

Martin
diff mbox series

Patch

Make full use of context-sensitive ranges in access warnings.

gcc/ChangeLog:

	* builtins.c (check_strncat_sizes): Pass access_data ctor additional
	arguments.
	(expand_builtin_memcmp): Move code to gimple-ssa-warn-access.cc.
	(expand_builtin_fork_or_exec): Same.
	* gimple-array-bounds.cc (array_bounds_checker::check_mem_ref):
	Pass compute_objsize additional arguments.
	(inbounds_memaccess_p): Same.
	(array_bounds_checker::check_array_bounds): Add an assert.  Stash
	statement in a member.
	(check_array_bounds_dom_walker::before_dom_children):
	* gimple-array-bounds.h (array_bounds_checker::m_stmt): New member.
	* gimple-ssa-sprintf.c (get_destination_size): Add an argument.
	(handle_printf_call): Pass a new argument.
	* gimple-ssa-warn-access.cc (get_size_range): Add an argument.
	(check_access): Add an argument and pass it along to callees.
	(check_read_access): Make a member function.
	(pass_waccess::check_strcat): Pass access_data ctor additional
	arguments.
	(pass_waccess::check_strncat): Same.
	(pass_waccess::check_stxcpy): Same.
	(pass_waccess::check_stxncpy): Same.
	(pass_waccess::check_strncmp): Same.
	(pass_waccess::check_read_access):
	(pass_waccess::check_builtin):
	(pass_waccess::maybe_check_access_sizes):
	(pass_waccess::maybe_check_dealloc_call):
	* gimple-ssa-warn-access.h (check_read_access): Declare a new
	member function.
	* pointer-query.cc (compute_objsize_r): Add an argument.
	(gimple_call_return_array): Same.
	(gimple_call_alloc_size): Same.
	(access_ref::access_ref): Same.
	(access_ref::get_ref): Same.
	(pointer_query::get_ref): Same.
	(handle_min_max_size): Pass an arguments to callees.
	(handle_array_ref): Add an argument.
	(handle_mem_ref): Same.
	(compute_objsize): Same.
	* pointer-query.h (struct access_ref): Adjust signatures.
	(struct access_data): Same.
	(gimple_call_alloc_size): Add an argument.
	(gimple_parm_array_size): Same.
	(compute_objsize): Same.
	* tree-ssa-strlen.c (strlen_pass::adjust_last_stmt): Pass an additional
	argument to compute_objsize.
	(strlen_pass::maybe_warn_overflow): Same.
	(maybe_diag_stxncpy_trunc): Same.

gcc/testsuite/ChangeLog:

	* gcc.dg/Wstringop-overflow-22.c: Correct typos.
	* gcc.dg/Wstringop-overflow-77.c: New test.

libstdc++/testsuite/ChangeLog:

	* 21_strings/basic_string/capacity/1.cc: Also suppress
	-Wstringop-overread.
	* 27_io/filesystem/path/factory/u8path-char8_t.cc: Same.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index f1c3fea3583..7d0f61fc98b 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -3600,7 +3600,7 @@  check_strncat_sizes (tree exp, tree objsize)
   /* Try to verify that the destination is big enough for the shortest
      string.  */
 
-  access_data data (exp, access_read_write, maxread, true);
+  access_data data (nullptr, exp, access_read_write, maxread, true);
   if (!objsize && warn_stringop_overflow)
     {
       /* If it hasn't been provided by __strncat_chk, try to determine
@@ -4260,12 +4260,6 @@  expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
   tree arg2 = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  /* Diagnose calls where the specified length exceeds the size of either
-     object.  */
-  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;
@@ -5486,27 +5480,6 @@  expand_builtin_fork_or_exec (tree fn, tree exp, rtx target, int ignore)
   tree id, decl;
   tree call;
 
-  if (DECL_FUNCTION_CODE (fn) != BUILT_IN_FORK)
-    {
-      tree path = CALL_EXPR_ARG (exp, 0);
-      /* Detect unterminated path.  */
-      if (!check_read_access (exp, path))
-	return NULL_RTX;
-
-      /* Also detect unterminated first argument.  */
-      switch (DECL_FUNCTION_CODE (fn))
-	{
-	case BUILT_IN_EXECL:
-	case BUILT_IN_EXECLE:
-	case BUILT_IN_EXECLP:
-	  if (!check_read_access (exp, path))
-	    return NULL_RTX;
-	default:
-	  break;
-	}
-    }
-
-
   /* If we are not profiling, just call the function.  */
   if (!profile_arc_flag)
     return NULL_RTX;
diff --git a/gcc/gimple-array-bounds.cc b/gcc/gimple-array-bounds.cc
index 0517e5ddd8e..a3535598998 100644
--- a/gcc/gimple-array-bounds.cc
+++ b/gcc/gimple-array-bounds.cc
@@ -426,7 +426,7 @@  array_bounds_checker::check_mem_ref (location_t location, tree ref,
       axssize = wi::to_offset (access_size);
 
   access_ref aref;
-  if (!compute_objsize (ref, 0, &aref, ranges))
+  if (!compute_objsize (ref, m_stmt, 0, &aref, ranges))
     return false;
 
   if (aref.offset_in_range (axssize))
@@ -667,7 +667,7 @@  array_bounds_checker::check_addr_expr (location_t location, tree t,
    problems discussed in pr98266 and pr97595.  */
 
 static bool
-inbounds_memaccess_p (tree t)
+inbounds_memaccess_p (tree t, gimple *stmt)
 {
   if (TREE_CODE (t) != COMPONENT_REF)
     return false;
@@ -686,7 +686,7 @@  inbounds_memaccess_p (tree t)
      allocated).  */
   access_ref aref;   // unused
   tree refop = TREE_OPERAND (mref, 0);
-  tree refsize = compute_objsize (refop, 1, &aref);
+  tree refsize = compute_objsize (refop, stmt, 1, &aref);
   if (!refsize || TREE_CODE (refsize) != INTEGER_CST)
     return false;
 
@@ -724,6 +724,7 @@  array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree,
 {
   tree t = *tp;
   struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
+
   location_t location;
 
   if (EXPR_HAS_LOCATION (t))
@@ -735,6 +736,8 @@  array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree,
 
   bool warned = false;
   array_bounds_checker *checker = (array_bounds_checker *) wi->info;
+  gcc_assert (checker->m_stmt == wi->stmt);
+
   if (TREE_CODE (t) == ARRAY_REF)
     warned = checker->check_array_ref (location, t, wi->stmt,
 				       false/*ignore_off_by_one*/);
@@ -746,7 +749,7 @@  array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree,
       checker->check_addr_expr (location, t, wi->stmt);
       *walk_subtree = false;
     }
-  else if (inbounds_memaccess_p (t))
+  else if (inbounds_memaccess_p (t, wi->stmt))
     /* Hack: Skip MEM_REF checks in accesses to a member of a base class
        at an offset that's within the bounds of the enclosing object.
        See pr98266 and pr97595.  */
@@ -794,14 +797,13 @@  check_array_bounds_dom_walker::before_dom_children (basic_block bb)
   for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
     {
       gimple *stmt = gsi_stmt (si);
-      struct walk_stmt_info wi;
       if (!gimple_has_location (stmt)
 	  || is_gimple_debug (stmt))
 	continue;
 
-      memset (&wi, 0, sizeof (wi));
-
+      struct walk_stmt_info wi{ };
       wi.info = checker;
+      checker->m_stmt = stmt;
 
       walk_gimple_op (stmt, array_bounds_checker::check_array_bounds, &wi);
     }
diff --git a/gcc/gimple-array-bounds.h b/gcc/gimple-array-bounds.h
index d8f7ff7a89f..d0e665eff11 100644
--- a/gcc/gimple-array-bounds.h
+++ b/gcc/gimple-array-bounds.h
@@ -36,8 +36,12 @@  private:
   void check_addr_expr (location_t, tree, gimple *);
   const value_range *get_value_range (const_tree op, gimple *);
 
+  /* Current function.  */
   struct function *fun;
+  /* Ranger instance.  */
   range_query *ranges;
+  /* Current statement.  */
+  gimple *m_stmt;
 };
 
 #endif // GCC_GIMPLE_ARRAY_BOUNDS_H
diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c
index 8e90b7cfc43..8f42cb59540 100644
--- a/gcc/gimple-ssa-sprintf.c
+++ b/gcc/gimple-ssa-sprintf.c
@@ -4030,11 +4030,11 @@  compute_format_length (call_info &info, format_result *res, range_query *query)
   return success;
 }
 
-/* Return the size of the object referenced by the expression DEST if
-   available, or the maximum possible size otherwise.  */
+/* Return the size of the object referenced by the expression DEST in
+   statement STMT, if available, or the maximum possible size otherwise.  */
 
 static unsigned HOST_WIDE_INT
-get_destination_size (tree dest, pointer_query &ptr_qry)
+get_destination_size (tree dest, gimple *stmt, pointer_query &ptr_qry)
 {
   /* When there is no destination return the maximum.  */
   if (!dest)
@@ -4042,7 +4042,7 @@  get_destination_size (tree dest, pointer_query &ptr_qry)
 
   /* Use compute_objsize to determine the size of the destination object.  */
   access_ref aref;
-  if (!ptr_qry.get_ref (dest, &aref))
+  if (!ptr_qry.get_ref (dest, stmt, &aref))
     return HOST_WIDE_INT_MAX;
 
   offset_int remsize = aref.size_remaining ();
@@ -4516,7 +4516,7 @@  handle_printf_call (gimple_stmt_iterator *gsi, pointer_query &ptr_qry)
       /* For non-bounded functions like sprintf, determine the size
 	 of the destination from the object or pointer passed to it
 	 as the first argument.  */
-      dstsize = get_destination_size (dstptr, ptr_qry);
+      dstsize = get_destination_size (dstptr, info.callstmt, ptr_qry);
     }
   else if (tree size = gimple_call_arg (info.callstmt, idx_dstsize))
     {
diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index 00c3ea0f505..d3616ef725f 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -1190,11 +1190,11 @@  warn_for_access (location_t loc, tree func, tree expr, int opt,
    by BNDRNG if nonnull and valid.  */
 
 static void
-get_size_range (range_query *query, tree bound, tree range[2],
+get_size_range (range_query *query, tree bound, gimple *stmt, tree range[2],
 		const offset_int bndrng[2])
 {
   if (bound)
-    get_size_range (query, bound, NULL, range);
+    get_size_range (query, bound, stmt, range);
 
   if (!bndrng || (bndrng[0] == 0 && bndrng[1] == HOST_WIDE_INT_M1U))
     return;
@@ -1251,7 +1251,8 @@  template <class GimpleOrTree>
 static bool
 check_access (GimpleOrTree exp, tree dstwrite,
 	      tree maxread, tree srcstr, tree dstsize,
-	      access_mode mode, const access_data *pad /* = NULL */)
+	      access_mode mode, const access_data *pad,
+	      range_query *rvals)
 {
   /* The size of the largest object is half the address space, or
      PTRDIFF_MAX.  (This is way too permissive.)  */
@@ -1338,7 +1339,8 @@  check_access (GimpleOrTree exp, tree dstwrite,
 
   /* Set RANGE to that of DSTWRITE if non-null, bounded by PAD->DST.BNDRNG
      if valid.  */
-  get_size_range (NULL, dstwrite, range, pad ? pad->dst.bndrng : NULL);
+  gimple *stmt = pad ? pad->stmt : nullptr;
+  get_size_range (rvals, dstwrite, stmt, range, pad ? pad->dst.bndrng : NULL);
 
   tree func = get_callee_fndecl (exp);
   /* Read vs write access by built-ins can be determined from the const
@@ -1432,7 +1434,7 @@  check_access (GimpleOrTree exp, tree dstwrite,
     {
       /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
 	 PAD is nonnull and BNDRNG is valid.  */
-      get_size_range (NULL, maxread, range, pad ? pad->src.bndrng : NULL);
+      get_size_range (rvals, maxread, stmt, range, pad ? pad->src.bndrng : NULL);
 
       location_t loc = get_location (exp);
       tree size = dstsize;
@@ -1479,7 +1481,7 @@  check_access (GimpleOrTree exp, tree dstwrite,
     {
       /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
 	 PAD is nonnull and BNDRNG is valid.  */
-      get_size_range (NULL, maxread, range, pad ? pad->src.bndrng : NULL);
+      get_size_range (rvals, maxread, stmt, 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]);
@@ -1512,13 +1514,14 @@  check_access (GimpleOrTree exp, tree dstwrite,
   return true;
 }
 
-bool
+static bool
 check_access (gimple *stmt, tree dstwrite,
 	      tree maxread, tree srcstr, tree dstsize,
-	      access_mode mode, const access_data *pad /* = NULL */)
+	      access_mode mode, const access_data *pad,
+	      range_query *rvals)
 {
-  return check_access<gimple *>(stmt, dstwrite, maxread, srcstr, dstsize,
-				mode, pad);
+  return check_access<gimple *> (stmt, dstwrite, maxread, srcstr, dstsize,
+				 mode, pad, rvals);
 }
 
 bool
@@ -1526,45 +1529,8 @@  check_access (tree expr, tree dstwrite,
 	      tree maxread, tree srcstr, tree dstsize,
 	      access_mode mode, const access_data *pad /* = NULL */)
 {
-  return check_access<tree>(expr, dstwrite, maxread, srcstr, dstsize,
-			    mode, pad);
-}
-
-/* A convenience wrapper for check_access above to check access
-   by a read-only function like puts.  */
-
-template <class GimpleOrTree>
-static bool
-check_read_access (GimpleOrTree expr, tree src, tree bound, int ost)
-{
-  if (!warn_stringop_overread)
-    return true;
-
-  if (bound && !useless_type_conversion_p (size_type_node, TREE_TYPE (bound)))
-    bound = fold_convert (size_type_node, bound);
-
-  tree fndecl = get_callee_fndecl (expr);
-  maybe_warn_nonstring_arg (fndecl, expr);
-
-  access_data data (expr, access_read_only, NULL_TREE, false, bound, true);
-  compute_objsize (src, ost, &data.src);
-  return check_access (expr, /*dstwrite=*/ NULL_TREE, /*maxread=*/ bound,
-		       /*srcstr=*/ src, /*dstsize=*/ NULL_TREE, data.mode,
-		       &data);
-}
-
-bool
-check_read_access (gimple *stmt, tree src, tree bound /* = NULL_TREE */,
-		   int ost /* = 1 */)
-{
-  return check_read_access<gimple *>(stmt, src, bound, ost);
-}
-
-bool
-check_read_access (tree expr, tree src, tree bound /* = NULL_TREE */,
-		   int ost /* = 1 */)
-{
-  return check_read_access<tree>(expr, src, bound, ost);
+  return check_access<tree> (expr, dstwrite, maxread, srcstr, dstsize,
+			     mode, pad, nullptr);
 }
 
 /* Return true if STMT is a call to an allocation function.  Unless
@@ -2130,6 +2096,7 @@  private:
   void check_stxncpy (gcall *);
   void check_strncmp (gcall *);
   void check_memop_access (gimple *, tree, tree, tree);
+  void check_read_access (gimple *, tree, tree = NULL_TREE, int = 1);
 
   void maybe_check_dealloc_call (gcall *);
   void maybe_check_access_sizes (rdwr_map *, tree, tree, gimple *);
@@ -2425,14 +2392,14 @@  pass_waccess::check_strcat (gcall *stmt)
      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 (stmt, access_read_write, NULL_TREE, true,
-		    NULL_TREE, true);
+  access_data data (m_ptr_qry.rvals, stmt, 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, &m_ptr_qry);
-  tree destsize = compute_objsize (dest, ost, &data.dst, &m_ptr_qry);
+  compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry);
+  tree destsize = compute_objsize (dest, stmt, ost, &data.dst, &m_ptr_qry);
 
   check_access (stmt, /*dstwrite=*/NULL_TREE, /*maxread=*/NULL_TREE,
-		src, destsize, data.mode, &data);
+		src, destsize, data.mode, &data, m_ptr_qry.rvals);
 }
 
 /* Check a call STMT to strcat() for overflow and warn if it does.  */
@@ -2466,12 +2433,12 @@  pass_waccess::check_strncat (gcall *stmt)
       maxlen = lendata.maxbound;
     }
 
-  access_data data (stmt, access_read_write);
+  access_data data (m_ptr_qry.rvals, stmt, 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.  */
   const int ost = warn_stringop_overflow - 1;
-  tree destsize = compute_objsize (dest, ost, &data.dst, &m_ptr_qry);
+  tree destsize = compute_objsize (dest, stmt, ost, &data.dst, &m_ptr_qry);
 
   /* Add one for the terminating nul.  */
   tree srclen = (maxlen
@@ -2500,7 +2467,7 @@  pass_waccess::check_strncat (gcall *stmt)
     srclen = maxread;
 
   check_access (stmt, /*dstwrite=*/NULL_TREE, maxread, srclen,
-		destsize, data.mode, &data);
+		destsize, data.mode, &data, m_ptr_qry.rvals);
 }
 
 /* Check a call STMT to stpcpy() or strcpy() for overflow and warn
@@ -2524,14 +2491,14 @@  pass_waccess::check_stxcpy (gcall *stmt)
 
   if (warn_stringop_overflow)
     {
-      access_data data (stmt, access_read_write, NULL_TREE, true,
-			NULL_TREE, true);
+      access_data data (m_ptr_qry.rvals, stmt, 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, &m_ptr_qry);
-      tree dstsize = compute_objsize (dst, ost, &data.dst, &m_ptr_qry);
+      compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry);
+      tree dstsize = compute_objsize (dst, stmt, ost, &data.dst, &m_ptr_qry);
       check_access (stmt, /*dstwrite=*/ NULL_TREE,
 		    /*maxread=*/ NULL_TREE, /*srcstr=*/ src,
-		    dstsize, data.mode, &data);
+		    dstsize, data.mode, &data, m_ptr_qry.rvals);
     }
 
   /* Check to see if the argument was declared attribute nonstring
@@ -2555,13 +2522,14 @@  pass_waccess::check_stxncpy (gcall *stmt)
   /* The number of bytes to write (not the maximum).  */
   tree len = call_arg (stmt, 2);
 
-  access_data data (stmt, access_read_write, len, true, len, true);
+  access_data data (m_ptr_qry.rvals, stmt, access_read_write, len, true, len,
+		    true);
   const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
-  compute_objsize (src, ost, &data.src, &m_ptr_qry);
-  tree dstsize = compute_objsize (dst, ost, &data.dst, &m_ptr_qry);
+  compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry);
+  tree dstsize = compute_objsize (dst, stmt, ost, &data.dst, &m_ptr_qry);
 
-  check_access (stmt, /*dstwrite=*/len,
-		/*maxread=*/len, src, dstsize, data.mode, &data);
+  check_access (stmt, /*dstwrite=*/len, /*maxread=*/len, src, dstsize,
+		data.mode, &data, m_ptr_qry.rvals);
 }
 
 /* Check a call STMT to stpncpy() or strncpy() for overflow and warn
@@ -2594,6 +2562,11 @@  pass_waccess::check_strncmp (gcall *stmt)
   tree len1 = c_strlen (arg1, 1, &lendata1);
   tree len2 = c_strlen (arg2, 1, &lendata2);
 
+  if (len1 && TREE_CODE (len1) != INTEGER_CST)
+    len1 = NULL_TREE;
+  if (len2 && TREE_CODE (len2) != INTEGER_CST)
+    len2 = NULL_TREE;
+
   if (len1 && len2)
     /* If the length of both arguments was computed they must both be
        nul-terminated and no further checking is necessary regardless
@@ -2606,13 +2579,15 @@  pass_waccess::check_strncmp (gcall *stmt)
   if (maybe_warn_nonstring_arg (get_callee_fndecl (stmt), stmt))
     return;
 
-  access_data adata1 (stmt, access_read_only, NULL_TREE, false, bound, true);
-  access_data adata2 (stmt, access_read_only, NULL_TREE, false, bound, true);
+  access_data adata1 (m_ptr_qry.rvals, stmt, access_read_only, NULL_TREE, false,
+		      bound, true);
+  access_data adata2 (m_ptr_qry.rvals, stmt, access_read_only, NULL_TREE, false,
+		      bound, true);
 
   /* Determine the range of the bound first and bail if it fails; it's
      cheaper than computing the size of the objects.  */
   tree bndrng[2] = { NULL_TREE, NULL_TREE };
-  get_size_range (m_ptr_qry.rvals, bound, bndrng, adata1.src.bndrng);
+  get_size_range (m_ptr_qry.rvals, bound, stmt, bndrng, adata1.src.bndrng);
   if (!bndrng[0] || integer_zerop (bndrng[0]))
     return;
 
@@ -2623,8 +2598,8 @@  pass_waccess::check_strncmp (gcall *stmt)
 
   /* compute_objsize almost never fails (and ultimately should never
      fail).  Don't bother to handle the rare case when it does.  */
-  if (!compute_objsize (arg1, 1, &adata1.src, &m_ptr_qry)
-      || !compute_objsize (arg2, 1, &adata2.src, &m_ptr_qry))
+  if (!compute_objsize (arg1, stmt, 1, &adata1.src, &m_ptr_qry)
+      || !compute_objsize (arg2, stmt, 1, &adata2.src, &m_ptr_qry))
     return;
 
   /* Compute the size of the remaining space in each array after
@@ -2672,15 +2647,41 @@  pass_waccess::check_memop_access (gimple *stmt, 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 (stmt, access_read_write);
+  access_data data (m_ptr_qry.rvals, stmt, access_read_write);
   tree srcsize
-    = src ? compute_objsize (src, 0, &data.src, &m_ptr_qry) : NULL_TREE;
-  tree dstsize = compute_objsize (dest, 0, &data.dst, &m_ptr_qry);
+    = src ? compute_objsize (src, stmt, 0, &data.src, &m_ptr_qry) : NULL_TREE;
+  tree dstsize = compute_objsize (dest, stmt, 0, &data.dst, &m_ptr_qry);
+
+  check_access (stmt, size, /*maxread=*/NULL_TREE, srcsize, dstsize,
+		data.mode, &data, m_ptr_qry.rvals);
+}
+
+/* A convenience wrapper for check_access to check access by a read-only
+   function like puts or strcmp.  */
+
+void
+pass_waccess::check_read_access (gimple *stmt, tree src,
+				 tree bound /* = NULL_TREE */,
+				 int ost /* = 1 */)
+{
+  if (!warn_stringop_overread)
+    return;
+
+  if (bound && !useless_type_conversion_p (size_type_node, TREE_TYPE (bound)))
+    bound = fold_convert (size_type_node, bound);
+
+  tree fndecl = get_callee_fndecl (stmt);
+  maybe_warn_nonstring_arg (fndecl, stmt);
 
-  check_access (stmt, size, /*maxread=*/NULL_TREE,
-		srcsize, dstsize, data.mode, &data);
+  access_data data (m_ptr_qry.rvals, stmt, access_read_only, NULL_TREE,
+		    false, bound, true);
+  compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry);
+  check_access (stmt, /*dstwrite=*/ NULL_TREE, /*maxread=*/ bound,
+		/*srcstr=*/ src, /*dstsize=*/ NULL_TREE, data.mode,
+		&data, m_ptr_qry.rvals);
 }
 
+
 /* Check call STMT to a built-in function for invalid accesses.  Return
    true if a call has been handled.  */
 
@@ -2699,6 +2700,15 @@  pass_waccess::check_builtin (gcall *stmt)
       check_alloca (stmt);
       return true;
 
+    case BUILT_IN_EXECL:
+    case BUILT_IN_EXECLE:
+    case BUILT_IN_EXECLP:
+    case BUILT_IN_EXECV:
+    case BUILT_IN_EXECVE:
+    case BUILT_IN_EXECVP:
+      check_read_access (stmt, call_arg (stmt, 0));
+      return true;
+
     case BUILT_IN_GETTEXT:
     case BUILT_IN_PUTS:
     case BUILT_IN_PUTS_UNLOCKED:
@@ -2721,8 +2731,12 @@  pass_waccess::check_builtin (gcall *stmt)
 
     case BUILT_IN_STRNDUP:
     case BUILT_IN_STRNLEN:
-      check_read_access (stmt, call_arg (stmt, 0), call_arg (stmt, 1));
-      return true;
+      {
+	tree str = call_arg (stmt, 0);
+	tree len = call_arg (stmt, 1);
+	check_read_access (stmt, str, len);
+	return true;
+      }
 
     case BUILT_IN_STRCAT:
       check_strcat (stmt);
@@ -2900,7 +2914,7 @@  pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
       /* Format the value or range to avoid an explosion of messages.  */
       char sizstr[80];
       tree sizrng[2] = { size_zero_node, build_all_ones_cst (sizetype) };
-      if (get_size_range (m_ptr_qry.rvals, access_size, NULL, sizrng, 1))
+      if (get_size_range (m_ptr_qry.rvals, access_size, stmt, sizrng, 1))
 	{
 	  char *s0 = print_generic_expr_to_str (sizrng[0]);
 	  if (tree_int_cst_equal (sizrng[0], sizrng[1]))
@@ -3028,11 +3042,11 @@  pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
 	    }
 	}
 
-      access_data data (ptr, access.second.mode, NULL_TREE, false,
-			NULL_TREE, false);
+      access_data data (m_ptr_qry.rvals, stmt, access.second.mode,
+			NULL_TREE, false, NULL_TREE, false);
       access_ref* const pobj = (access.second.mode == access_write_only
 				? &data.dst : &data.src);
-      tree objsize = compute_objsize (ptr, 1, pobj, &m_ptr_qry);
+      tree objsize = compute_objsize (ptr, stmt, 1, pobj, &m_ptr_qry);
 
       /* The size of the destination or source object.  */
       tree dstsize = NULL_TREE, srcsize = NULL_TREE;
@@ -3064,7 +3078,7 @@  pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
       if (mode == access_deferred)
 	mode = TYPE_READONLY (argtype) ? access_read_only : access_read_write;
       check_access (stmt, access_size, /*maxread=*/ NULL_TREE, srcsize,
-		    dstsize, mode, &data);
+		    dstsize, mode, &data, m_ptr_qry.rvals);
 
       if (warning_suppressed_p (stmt, OPT_Wstringop_overflow_))
 	opt_warned = OPT_Wstringop_overflow_;
@@ -3187,7 +3201,7 @@  pass_waccess::maybe_check_dealloc_call (gcall *call)
     return;
 
   access_ref aref;
-  if (!compute_objsize (ptr, 0, &aref, &m_ptr_qry))
+  if (!compute_objsize (ptr, call, 0, &aref, &m_ptr_qry))
     return;
 
   tree ref = aref.ref;
diff --git a/gcc/gimple-ssa-warn-access.h b/gcc/gimple-ssa-warn-access.h
index 00f5bb1a7b2..124f3f530d3 100644
--- a/gcc/gimple-ssa-warn-access.h
+++ b/gcc/gimple-ssa-warn-access.h
@@ -45,7 +45,4 @@  class access_data;
 extern bool check_access (tree, tree, tree, tree, tree, access_mode,
 			  const access_data * = NULL);
 
-extern bool check_read_access (gimple *, tree, tree = NULL_TREE, int ost = 1);
-extern bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
-
 #endif   // GCC_GIMPLE_SSA_WARN_ACCESS_H
diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc
index 910f452868e..3b9f970846b 100644
--- a/gcc/pointer-query.cc
+++ b/gcc/pointer-query.cc
@@ -43,8 +43,8 @@ 
 #include "tree-ssanames.h"
 #include "target.h"
 
-static bool compute_objsize_r (tree, int, access_ref *, ssa_name_limit_t &,
-			       pointer_query *);
+static bool compute_objsize_r (tree, gimple *, int, access_ref *,
+			       ssa_name_limit_t &, pointer_query *);
 
 /* Wrapper around the wide_int overload of get_range that accepts
    offset_int instead.  For middle end expressions returns the same
@@ -115,7 +115,7 @@  get_offset_range (tree x, gimple *stmt, offset_int r[2], range_query *rvals)
 
 static tree
 gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
-			  range_query *rvals)
+			  ssa_name_limit_t &snlim, pointer_query *qry)
 {
   /* Clear and set below for the rare function(s) that might return
      a past-the-end pointer.  */
@@ -191,7 +191,7 @@  gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
 	offrng[0] = 0;
 	offrng[1] = HOST_WIDE_INT_M1U;
 	tree off = gimple_call_arg (stmt, 2);
-	bool off_valid = get_offset_range (off, stmt, offrng, rvals);
+	bool off_valid = get_offset_range (off, stmt, offrng, qry->rvals);
 	if (!off_valid || offrng[0] != offrng[1])
 	  {
 	    /* If the offset is either indeterminate or in some range,
@@ -199,7 +199,7 @@  gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
 	       of the source object.  */
 	    access_ref aref;
 	    tree src = gimple_call_arg (stmt, 1);
-	    if (compute_objsize (src, 1, &aref, rvals)
+	    if (compute_objsize (src, stmt, 1, &aref, qry)
 		&& aref.sizrng[1] < offrng[1])
 	      offrng[1] = aref.sizrng[1];
 	  }
@@ -212,7 +212,7 @@  gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
     case BUILT_IN_MEMCHR:
       {
 	tree off = gimple_call_arg (stmt, 2);
-	if (get_offset_range (off, stmt, offrng, rvals))
+	if (get_offset_range (off, stmt, offrng, qry->rvals))
 	  offrng[1] -= 1;
 	else
 	  offrng[1] = HOST_WIDE_INT_M1U;
@@ -233,7 +233,7 @@  gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
       {
 	access_ref aref;
 	tree src = gimple_call_arg (stmt, 1);
-	if (compute_objsize (src, 1, &aref, rvals))
+	if (compute_objsize_r (src, stmt, 1, &aref, snlim, qry))
 	  offrng[1] = aref.sizrng[1] - 1;
 	else
 	  offrng[1] = HOST_WIDE_INT_M1U;
@@ -250,7 +250,7 @@  gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
 	   and the source object size.  */
 	offrng[1] = HOST_WIDE_INT_M1U;
 	tree off = gimple_call_arg (stmt, 2);
-	if (!get_offset_range (off, stmt, offrng, rvals)
+	if (!get_offset_range (off, stmt, offrng, qry->rvals)
 	    || offrng[0] != offrng[1])
 	  {
 	    /* If the offset is either indeterminate or in some range,
@@ -258,7 +258,7 @@  gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
 	       of the source object.  */
 	    access_ref aref;
 	    tree src = gimple_call_arg (stmt, 1);
-	    if (compute_objsize (src, 1, &aref, rvals)
+	    if (compute_objsize_r (src, stmt, 1, &aref, snlim, qry)
 		&& aref.sizrng[1] < offrng[1])
 	      offrng[1] = aref.sizrng[1];
 	  }
@@ -445,7 +445,7 @@  get_size_range (tree exp, tree range[2], int flags /* = 0 */)
 
 tree
 gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
-			range_query * /* = NULL */)
+			range_query *qry /* = NULL */)
 {
   if (!stmt || !is_gimple_call (stmt))
     return NULL_TREE;
@@ -503,7 +503,7 @@  gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
   {
     tree r[2];
     /* Determine the largest valid range size, including zero.  */
-    if (!get_size_range (size, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
+    if (!get_size_range (qry, size, stmt, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
       return NULL_TREE;
     rng1[0] = wi::to_wide (r[0], prec);
     rng1[1] = wi::to_wide (r[1], prec);
@@ -519,7 +519,7 @@  gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
   {
     tree r[2];
       /* As above, use the full non-negative range on failure.  */
-    if (!get_size_range (n, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
+    if (!get_size_range (qry, n, stmt, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
       return NULL_TREE;
     rng2[0] = wi::to_wide (r[0], prec);
     rng2[1] = wi::to_wide (r[1], prec);
@@ -546,7 +546,7 @@  gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
    Set STATIC_ARRAY if the array parameter has been declared [static].
    Return the function parameter on success and null otherwise.  */
 
-tree
+static tree
 gimple_parm_array_size (tree ptr, wide_int rng[2],
 			bool *static_array /* = NULL */)
 {
@@ -596,10 +596,17 @@  gimple_parm_array_size (tree ptr, wide_int rng[2],
   return var;
 }
 
-access_ref::access_ref (tree bound /* = NULL_TREE */,
+/* Given a statement STMT, set the bounds of the reference to at most
+   as many bytes as BOUND or unknown when null, and at least one when
+   the MINACCESS is true unless BOUND is a constant zero.  STMT is
+   used for context to get accurate range info.  */
+
+access_ref::access_ref (range_query *qry /* = nullptr */,
+			tree bound /* = NULL_TREE */,
+			gimple *stmt /* = nullptr */,
 			bool minaccess /* = false */)
-: ref (), eval ([](tree x){ return x; }), deref (), trail1special (true),
-  base0 (true), parmarray ()
+  : ref (), eval ([](tree x){ return x; }), deref (), trail1special (true),
+    base0 (true), parmarray ()
 {
   /* Set to valid.  */
   offrng[0] = offrng[1] = 0;
@@ -615,7 +622,7 @@  access_ref::access_ref (tree bound /* = NULL_TREE */,
      set the bounds of the access to reflect both it and MINACCESS.
      BNDRNG[0] is the size of the minimum access.  */
   tree rng[2];
-  if (bound && get_size_range (bound, rng, SR_ALLOW_ZERO))
+  if (bound && get_size_range (qry, bound, stmt, rng, SR_ALLOW_ZERO))
     {
       bndrng[0] = wi::to_offset (rng[0]);
       bndrng[1] = wi::to_offset (rng[1]);
@@ -696,7 +703,8 @@  access_ref::get_ref (vec<access_ref> *all_refs,
     {
       access_ref phi_arg_ref;
       tree arg = gimple_phi_arg_def (phi_stmt, i);
-      if (!compute_objsize_r (arg, ostype, &phi_arg_ref, *psnlim, qry)
+      if (!compute_objsize_r (arg, phi_stmt, ostype, &phi_arg_ref, *psnlim,
+			      qry)
 	  || phi_arg_ref.sizrng[0] < 0)
 	/* A PHI with all null pointer arguments.  */
 	return NULL_TREE;
@@ -1312,7 +1320,7 @@  pointer_query::get_ref (tree ptr, int ostype /* = 1 */) const
    there or compute it and insert it into the cache if it's nonnonull.  */
 
 bool
-pointer_query::get_ref (tree ptr, access_ref *pref, int ostype /* = 1 */)
+pointer_query::get_ref (tree ptr, gimple *stmt, access_ref *pref, int ostype /* = 1 */)
 {
   const unsigned version
     = TREE_CODE (ptr) == SSA_NAME ? SSA_NAME_VERSION (ptr) : 0;
@@ -1335,7 +1343,7 @@  pointer_query::get_ref (tree ptr, access_ref *pref, int ostype /* = 1 */)
       ++misses;
     }
 
-  if (!compute_objsize (ptr, ostype, pref, this))
+  if (!compute_objsize (ptr, stmt, ostype, pref, this))
     {
       ++failures;
       return false;
@@ -1502,7 +1510,7 @@  static bool
 handle_min_max_size (tree ptr, int ostype, access_ref *pref,
 		     ssa_name_limit_t &snlim, pointer_query *qry)
 {
-  const gimple *stmt = SSA_NAME_DEF_STMT (ptr);
+  gimple *stmt = SSA_NAME_DEF_STMT (ptr);
   const tree_code code = gimple_assign_rhs_code (stmt);
 
   /* In a valid MAX_/MIN_EXPR both operands must refer to the same array.
@@ -1512,7 +1520,7 @@  handle_min_max_size (tree ptr, int ostype, access_ref *pref,
      for the expression.  */
   access_ref aref[2] = { *pref, *pref };
   tree arg1 = gimple_assign_rhs1 (stmt);
-  if (!compute_objsize_r (arg1, ostype, &aref[0], snlim, qry))
+  if (!compute_objsize_r (arg1, stmt, ostype, &aref[0], snlim, qry))
     {
       aref[0].base0 = false;
       aref[0].offrng[0] = aref[0].offrng[1] = 0;
@@ -1521,7 +1529,7 @@  handle_min_max_size (tree ptr, int ostype, access_ref *pref,
     }
 
   tree arg2 = gimple_assign_rhs2 (stmt);
-  if (!compute_objsize_r (arg2, ostype, &aref[1], snlim, qry))
+  if (!compute_objsize_r (arg2, stmt, ostype, &aref[1], snlim, qry))
     {
       aref[1].base0 = false;
       aref[1].offrng[0] = aref[1].offrng[1] = 0;
@@ -1589,8 +1597,9 @@  handle_min_max_size (tree ptr, int ostype, access_ref *pref,
    on success and false on failure.  */
 
 static bool
-handle_array_ref (tree aref, bool addr, int ostype, access_ref *pref,
-		  ssa_name_limit_t &snlim, pointer_query *qry)
+handle_array_ref (tree aref, gimple *stmt, bool addr, int ostype,
+		  access_ref *pref, ssa_name_limit_t &snlim,
+		  pointer_query *qry)
 {
   gcc_assert (TREE_CODE (aref) == ARRAY_REF);
 
@@ -1603,7 +1612,7 @@  handle_array_ref (tree aref, bool addr, int ostype, access_ref *pref,
        of known bound.  */
     return false;
 
-  if (!compute_objsize_r (arefop, ostype, pref, snlim, qry))
+  if (!compute_objsize_r (arefop, stmt, ostype, pref, snlim, qry))
     return false;
 
   offset_int orng[2];
@@ -1668,7 +1677,7 @@  handle_array_ref (tree aref, bool addr, int ostype, access_ref *pref,
    MREF.  Return true on success and false on failure.  */
 
 static bool
-handle_mem_ref (tree mref, int ostype, access_ref *pref,
+handle_mem_ref (tree mref, gimple *stmt, int ostype, access_ref *pref,
 		ssa_name_limit_t &snlim, pointer_query *qry)
 {
   gcc_assert (TREE_CODE (mref) == MEM_REF);
@@ -1690,7 +1699,7 @@  handle_mem_ref (tree mref, int ostype, access_ref *pref,
     }
 
   tree mrefop = TREE_OPERAND (mref, 0);
-  if (!compute_objsize_r (mrefop, ostype, pref, snlim, qry))
+  if (!compute_objsize_r (mrefop, stmt, ostype, pref, snlim, qry))
     return false;
 
   offset_int orng[2];
@@ -1723,7 +1732,7 @@  handle_mem_ref (tree mref, int ostype, access_ref *pref,
    to influence code generation or optimization.  */
 
 static bool
-compute_objsize_r (tree ptr, int ostype, access_ref *pref,
+compute_objsize_r (tree ptr, gimple *stmt, int ostype, access_ref *pref,
 		   ssa_name_limit_t &snlim, pointer_query *qry)
 {
   STRIP_NOPS (ptr);
@@ -1774,7 +1783,7 @@  compute_objsize_r (tree ptr, int ostype, access_ref *pref,
   if (code == BIT_FIELD_REF)
     {
       tree ref = TREE_OPERAND (ptr, 0);
-      if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
+      if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
 	return false;
 
       offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2)));
@@ -1796,7 +1805,7 @@  compute_objsize_r (tree ptr, int ostype, access_ref *pref,
 	  /* In OSTYPE zero (for raw memory functions like memcpy), use
 	     the maximum size instead if the identity of the enclosing
 	     object cannot be determined.  */
-	  if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
+	  if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
 	    return false;
 
 	  /* Otherwise, use the size of the enclosing object and add
@@ -1850,15 +1859,15 @@  compute_objsize_r (tree ptr, int ostype, access_ref *pref,
     }
 
   if (code == ARRAY_REF)
-    return handle_array_ref (ptr, addr, ostype, pref, snlim, qry);
+    return handle_array_ref (ptr, stmt, addr, ostype, pref, snlim, qry);
 
   if (code == MEM_REF)
-    return handle_mem_ref (ptr, ostype, pref, snlim, qry);
+    return handle_mem_ref (ptr, stmt, ostype, pref, snlim, qry);
 
   if (code == TARGET_MEM_REF)
     {
       tree ref = TREE_OPERAND (ptr, 0);
-      if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
+      if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
 	return false;
 
       /* TODO: Handle remaining operands.  Until then, add maximum offset.  */
@@ -1903,7 +1912,7 @@  compute_objsize_r (tree ptr, int ostype, access_ref *pref,
   if (code == POINTER_PLUS_EXPR)
     {
       tree ref = TREE_OPERAND (ptr, 0);
-      if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
+      if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
 	return false;
 
       /* Clear DEREF since the offset is being applied to the target
@@ -1922,7 +1931,7 @@  compute_objsize_r (tree ptr, int ostype, access_ref *pref,
   if (code == VIEW_CONVERT_EXPR)
     {
       ptr = TREE_OPERAND (ptr, 0);
-      return compute_objsize_r (ptr, ostype, pref, snlim, qry);
+      return compute_objsize_r (ptr, stmt, ostype, pref, snlim, qry);
     }
 
   if (code == SSA_NAME)
@@ -1951,7 +1960,7 @@  compute_objsize_r (tree ptr, int ostype, access_ref *pref,
 	    }
 	}
 
-      gimple *stmt = SSA_NAME_DEF_STMT (ptr);
+      stmt = SSA_NAME_DEF_STMT (ptr);
       if (is_gimple_call (stmt))
 	{
 	  /* If STMT is a call to an allocation function get the size
@@ -1979,9 +1988,9 @@  compute_objsize_r (tree ptr, int ostype, access_ref *pref,
 	      bool past_end;
 	      offset_int offrng[2];
 	      if (tree ret = gimple_call_return_array (stmt, offrng,
-						       &past_end, rvals))
+						       &past_end, snlim, qry))
 		{
-		  if (!compute_objsize_r (ret, ostype, pref, snlim, qry))
+		  if (!compute_objsize_r (ret, stmt, ostype, pref, snlim, qry))
 		    return false;
 
 		  /* Cap OFFRNG[1] to at most the remaining size of
@@ -2076,14 +2085,14 @@  compute_objsize_r (tree ptr, int ostype, access_ref *pref,
       if (code == ASSERT_EXPR)
 	{
 	  rhs = TREE_OPERAND (rhs, 0);
-	  return compute_objsize_r (rhs, ostype, pref, snlim, qry);
+	  return compute_objsize_r (rhs, stmt, ostype, pref, snlim, qry);
 	}
 
       if (code == POINTER_PLUS_EXPR
 	  && TREE_CODE (TREE_TYPE (rhs)) == POINTER_TYPE)
 	{
 	  /* Compute the size of the object first. */
-	  if (!compute_objsize_r (rhs, ostype, pref, snlim, qry))
+	  if (!compute_objsize_r (rhs, stmt, ostype, pref, snlim, qry))
 	    return false;
 
 	  offset_int orng[2];
@@ -2099,7 +2108,7 @@  compute_objsize_r (tree ptr, int ostype, access_ref *pref,
 
       if (code == ADDR_EXPR || code == SSA_NAME)
 	{
-	  if (!compute_objsize_r (rhs, ostype, pref, snlim, qry))
+	  if (!compute_objsize_r (rhs, stmt, ostype, pref, snlim, qry))
 	    return false;
 	  qry->put_ref (ptr, *pref);
 	  return true;
@@ -2128,18 +2137,21 @@  compute_objsize_r (tree ptr, int ostype, access_ref *pref,
    instead.  */
 
 tree
-compute_objsize (tree ptr, int ostype, access_ref *pref,
-		 range_query *rvals /* = NULL */)
+compute_objsize (tree ptr, gimple *stmt, int ostype, access_ref *pref,
+		 pointer_query *ptr_qry)
 {
   pointer_query qry;
-  qry.rvals = rvals;
+  if (ptr_qry)
+    ptr_qry->depth = 0;
+  else
+    ptr_qry = &qry;
 
   /* Clear and invalidate in case *PREF is being reused.  */
   pref->offrng[0] = pref->offrng[1] = 0;
   pref->sizrng[0] = pref->sizrng[1] = -1;
 
   ssa_name_limit_t snlim;
-  if (!compute_objsize_r (ptr, ostype, pref, snlim, &qry))
+  if (!compute_objsize_r (ptr, stmt, ostype, pref, snlim, ptr_qry))
     return NULL_TREE;
 
   offset_int maxsize = pref->size_remaining ();
@@ -2152,26 +2164,12 @@  compute_objsize (tree ptr, int ostype, access_ref *pref,
    transition to the pointer_query API.  */
 
 tree
-compute_objsize (tree ptr, int ostype, access_ref *pref, pointer_query *ptr_qry)
+compute_objsize (tree ptr, gimple *stmt, int ostype, access_ref *pref,
+		 range_query *rvals /* = NULL */)
 {
   pointer_query qry;
-  if (ptr_qry)
-    ptr_qry->depth = 0;
-  else
-    ptr_qry = &qry;
-
-  /* Clear and invalidate in case *PREF is being reused.  */
-  pref->offrng[0] = pref->offrng[1] = 0;
-  pref->sizrng[0] = pref->sizrng[1] = -1;
-
-  ssa_name_limit_t snlim;
-  if (!compute_objsize_r (ptr, ostype, pref, snlim, ptr_qry))
-    return NULL_TREE;
-
-  offset_int maxsize = pref->size_remaining ();
-  if (pref->base0 && pref->offrng[0] < 0 && pref->offrng[1] >= 0)
-    pref->offrng[0] = 0;
-  return wide_int_to_tree (sizetype, maxsize);
+  qry.rvals = rvals;
+  return compute_objsize (ptr, stmt, ostype, pref, &qry);
 }
 
 /* Legacy wrapper around the above.  The function should be removed
@@ -2184,7 +2182,7 @@  compute_objsize (tree ptr, int ostype, tree *pdecl /* = NULL */,
   /* Set the initial offsets to zero and size to negative to indicate
      none has been computed yet.  */
   access_ref ref;
-  tree size = compute_objsize (ptr, ostype, &ref, rvals);
+  tree size = compute_objsize (ptr, nullptr, ostype, &ref, rvals);
   if (!size || !ref.base0)
     return NULL_TREE;
 
diff --git a/gcc/pointer-query.h b/gcc/pointer-query.h
index 3c8172c652d..96c500132b6 100644
--- a/gcc/pointer-query.h
+++ b/gcc/pointer-query.h
@@ -60,18 +60,16 @@  class pointer_query;
 /* Describes a reference to an object used in an access.  */
 struct access_ref
 {
-  /* 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);
+  /* Set the bounds of the reference.  */
+  access_ref (range_query *query = nullptr, tree = NULL_TREE,
+	      gimple * = nullptr, bool = false);
 
   /* Return the PHI node REF refers to or null if it doesn't.  */
   gphi *phi () const;
 
   /* Return the object to which REF refers.  */
-  tree get_ref (vec<access_ref> *, access_ref * = NULL, int = 1,
-		ssa_name_limit_t * = NULL, pointer_query * = NULL) const;
+  tree get_ref (vec<access_ref> *, access_ref * = nullptr, int = 1,
+		ssa_name_limit_t * = nullptr, pointer_query * = nullptr) const;
 
   /* Return true if OFFRNG is the constant zero.  */
   bool offset_zero () const
@@ -85,7 +83,7 @@  struct access_ref
 
   /* Return the maximum amount of space remaining and if non-null, set
      argument to the minimum.  */
-  offset_int size_remaining (offset_int * = NULL) const;
+  offset_int size_remaining (offset_int * = nullptr) const;
 
 /* Return true if the offset and object size are in range for SIZE.  */
   bool offset_in_range (const offset_int &) const;
@@ -172,13 +170,13 @@  public:
   };
 
   /* Construct an object with the given Ranger instance and cache.  */
-  explicit pointer_query (range_query * = NULL, cache_type * = NULL);
+  explicit pointer_query (range_query * = nullptr, cache_type * = nullptr);
 
   /* Retrieve the access_ref for a variable from cache if it's there.  */
   const access_ref* get_ref (tree, int = 1) const;
 
   /* Retrieve the access_ref for a variable from cache or compute it.  */
-  bool get_ref (tree, access_ref*, int = 1);
+  bool get_ref (tree, gimple *, access_ref*, int = 1);
 
   /* Add an access_ref for the SSA_NAME to the cache.  */
   void put_ref (tree, const access_ref&, int = 1);
@@ -208,19 +206,23 @@  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 (gimple *stmt, access_mode mode,
+  access_data (range_query *query, gimple *stmt, access_mode mode,
 	       tree maxwrite = NULL_TREE, bool minwrite = false,
 	       tree maxread = NULL_TREE, bool minread = false)
     : stmt (stmt), call (),
-      dst (maxwrite, minwrite), src (maxread, minread), mode (mode) { }
+      dst (query, maxwrite, stmt, minwrite),
+      src (query, maxread, stmt, minread),
+      mode (mode) { }
 
   /* 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,
+  access_data (range_query *query, tree expr, access_mode mode,
 	       tree maxwrite = NULL_TREE, bool minwrite = false,
 	       tree maxread = NULL_TREE, bool minread = false)
     : stmt (), call (expr),
-      dst (maxwrite, minwrite), src (maxread, minread), mode (mode) { }
+      dst (query, maxwrite, nullptr, minwrite),
+      src (query, maxread, nullptr, minread),
+      mode (mode) { }
 
   /* Access statement.  */
   gimple *stmt;
@@ -245,14 +247,23 @@  extern bool get_size_range (tree, tree[2], int = 0);
 extern bool get_size_range (range_query *, tree, gimple *, tree[2], int = 0);
 
 class range_query;
-extern tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
-				    range_query * = NULL);
-extern tree gimple_parm_array_size (tree, wide_int[2], bool * = NULL);
+extern tree gimple_call_alloc_size (gimple *, wide_int[2] = nullptr,
+				    range_query * = nullptr);
+
+/* Compute the size of an object referenced by the first argument in
+   a statement given by second argument, using Object Size Type given
+   by third argument.  Store result in an access_ref.  */
+extern tree compute_objsize (tree, gimple *, int, access_ref *,
+			     range_query * = nullptr);
+extern tree compute_objsize (tree, gimple *, int, access_ref *,
+			     pointer_query *);
+inline tree compute_objsize (tree ptr, int ostype, access_ref *pref)
+{
+  return compute_objsize (ptr, nullptr, ostype, pref, (range_query *)nullptr);
+}
 
-extern tree compute_objsize (tree, int, access_ref *, range_query * = NULL);
 /* Legacy/transitional API.  Should not be used in new code.  */
-extern tree compute_objsize (tree, int, access_ref *, pointer_query *);
-extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
-			     range_query * = NULL);
+extern tree compute_objsize (tree, int, tree * = nullptr, tree * = nullptr,
+			     range_query * = nullptr);
 
 #endif   // GCC_POINTER_QUERY_H
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-22.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-22.c
index 8eaaa713275..764b1990276 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-22.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-22.c
@@ -260,13 +260,12 @@  T (puts_unlocked, a);       // { dg-warning "missing terminating nul" "puts_unlo
 
 // Exerise exec functions.
 T (execl, a, s, NULL);      // { dg-warning "missing terminating nul" "execl" }
-T (execl, a, s, NULL);      // { dg-warning "missing terminating nul" "execl" }
-T (execle, a, s, NULL, NULL);   // { dg-warning "missing terminating nul" "execl" }
-T (execlp, a, s, NULL);     // { dg-warning "missing terminating nul" "execl" }
+T (execle, a, s, NULL, NULL);   // { dg-warning "missing terminating nul" "execle" }
+T (execlp, a, s, NULL);     // { dg-warning "missing terminating nul" "execlp" }
 
-T (execv, a, &d);           // { dg-warning "missing terminating nul" "execl" }
-T (execve, a, &d, &d);      // { dg-warning "missing terminating nul" "execl" }
-T (execvp, a, &d);          // { dg-warning "missing terminating nul" "execl" }
+T (execv, a, &d);           // { dg-warning "missing terminating nul" "execv" }
+T (execve, a, &d, &d);      // { dg-warning "missing terminating nul" "execve" }
+T (execvp, a, &d);          // { dg-warning "missing terminating nul" "execvp" }
 
 T (gettext, a);             // { dg-warning "missing terminating nul" "gettext" }
 
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-77.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-77.c
new file mode 100644
index 00000000000..e8bc327722a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-77.c
@@ -0,0 +1,38 @@ 
+/* Verify that -Wstringop-overflow uses context-sensitive range info
+   even at -O0.
+   { dg-do compile }
+   { dg-options "-O0 -Wall" } */
+
+extern void* memset (void*, int, __SIZE_TYPE__);
+
+char a[8];
+
+void warn_offset_range (int i)
+{
+  if (i < 4)
+    i = 4;
+  memset (a + i, 0, 5);       // { dg-warning "writing 5 bytes into a region of size 4 " }
+}
+
+void warn_size_range (int i, int n)
+{
+  if (n < 5)
+    n = 5;
+
+  memset (a + 4, 1, n);      // { dg-warning "writing between 5 and \\d+ bytes into a region of size 4 " }
+}
+
+void warn_offset_and_size_range (int i, int n)
+{
+  if (n < 5)
+    n = 5;
+
+  if (i < 4)
+    {
+      if (n < 9)
+	n = 9;
+      memset (a + i, 1, n);   // { dg-warning "writing between 9 and \\d+ bytes into a region of size 8 " }
+    }
+  else
+    memset (a + i, 0, n);     // { dg-warning "writing between 5 and \\d+ bytes into a region of size 4 " }
+}
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 8c39869616f..2de7cb1a6a0 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -1833,7 +1833,7 @@  strlen_pass::adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat)
       tree dst = gimple_call_arg (last.stmt, 0);
 
       access_ref aref;
-      tree size = compute_objsize (dst, 1, &aref, &ptr_qry);
+      tree size = compute_objsize (dst, stmt, 1, &aref, &ptr_qry);
       if (size && tree_int_cst_lt (size, len))
 	return;
     }
@@ -2035,7 +2035,7 @@  strlen_pass::maybe_warn_overflow (gimple *stmt, bool call_lhs, tree len,
   access_ref aref;
   /* The size of the destination region (which is smaller than
      the destination object for stores at a non-zero offset).  */
-  tree destsize = compute_objsize (dest, ostype, &aref, &ptr_qry);
+  tree destsize = compute_objsize (dest, stmt, ostype, &aref, &ptr_qry);
 
   if (!destsize)
     {
@@ -3115,7 +3115,7 @@  maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt,
     }
 
   access_ref aref;
-  if (tree dstsize = compute_objsize (dst, 1, &aref, ptr_qry))
+  if (tree dstsize = compute_objsize (dst, stmt, 1, &aref, ptr_qry))
     {
       /* The source length is unknown.  Try to determine the destination
 	 size and see if it matches the specified bound.  If not, bail.
@@ -3130,7 +3130,7 @@  maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt,
       /* Avoid warning for strncpy(a, b, N) calls where the following
 	 equalities hold:
 	   N == sizeof a && N == sizeof b */
-      if (tree srcsize = compute_objsize (src, 1, &aref, ptr_qry))
+      if (tree srcsize = compute_objsize (src, stmt, 1, &aref, ptr_qry))
 	if (wi::to_wide (srcsize) == cntrange[1])
 	  return false;
 
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/1.cc b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/1.cc
index a2320f2b564..28059748ab7 100644
--- a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/1.cc
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/1.cc
@@ -17,7 +17,7 @@ 
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
-// { dg-options "-Wno-stringop-overflow" }
+// { dg-options "-Wno-stringop-overflow -Wstringop-overread" }
 
 // 21.3.3 string capacity
 
diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/factory/u8path-char8_t.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/factory/u8path-char8_t.cc
index f52900d7fda..f5d281fd25a 100644
--- a/libstdc++-v3/testsuite/27_io/filesystem/path/factory/u8path-char8_t.cc
+++ b/libstdc++-v3/testsuite/27_io/filesystem/path/factory/u8path-char8_t.cc
@@ -15,7 +15,7 @@ 
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
-// { dg-options "-fchar8_t" }
+// { dg-options "-fchar8_t -Wstringop-overread" }
 // { dg-do run { target c++17 } }
 
 #include <filesystem>
@@ -36,6 +36,7 @@  test01()
   p = fs::u8path(u8"\xf0\x9d\x84\x9e");
   VERIFY( p.u8string() == u8"\U0001D11E" );
 
+  // The following triggers a -Wstringop-overread.
   std::u8string s1 = u8"filename2";
   p = fs::u8path(s1);
   VERIFY( p.u8string() == u8"filename2" );