PR libfortran/103634 - Runtime crash with PACK on zero-sized arrays
Commit Message
Dear all,
I had thought that we had fixed this in the past (see PR31001),
but it did fail for me with all gcc versions I have tried (7-12)
for a slightly more elaborate case as in the old testcase.
The loop in pack_internal did try to access the first element of
the array argument to PACK even if one (or more) extents were zero.
This is not good.
Solution: check the extents and return early. (We already do a
related check for the vector argument if present).
Regtested on x86_64-pc-linux-gnu. OK for mainline?
As this segfaults on valid code at runtime: I am considering
backporting this, if there are no objections.
Thanks,
Harald
Comments
Hello,
On 09/12/2021 21:05, Harald Anlauf via Fortran wrote:
> Dear all,
>
> I had thought that we had fixed this in the past (see PR31001),
> but it did fail for me with all gcc versions I have tried (7-12)
> for a slightly more elaborate case as in the old testcase.
>
> The loop in pack_internal did try to access the first element of
> the array argument to PACK even if one (or more) extents were zero.
> This is not good.
>
> Solution: check the extents and return early. (We already do a
> related check for the vector argument if present).
If there is a vector argument, aren’t we supposed to copy it to the result ?
There is something else to pay attention for, the early return should
come at least after the return array bounds have been set. In the
testcase an array with the correct bounds has been allocated beforehand
to hold the return value, but it’s not always the case.
For what it’s worth, the non-generic variant in pack.m4 (or in
pack_{i,f,c}{1,2,4,8,10,16}.c) has a zero extent check and it clears the
source ptr in that case, which makes it setup the return array and then
jump to the vector copy at the end of the function.
Hi Mikael,
Am 09.12.21 um 21:37 schrieb Mikael Morin:
> Hello,
>
> On 09/12/2021 21:05, Harald Anlauf via Fortran wrote:
>> Dear all,
>>
>> I had thought that we had fixed this in the past (see PR31001),
>> but it did fail for me with all gcc versions I have tried (7-12)
>> for a slightly more elaborate case as in the old testcase.
>>
>> The loop in pack_internal did try to access the first element of
>> the array argument to PACK even if one (or more) extents were zero.
>> This is not good.
>>
>> Solution: check the extents and return early. (We already do a
>> related check for the vector argument if present).
>
> If there is a vector argument, aren’t we supposed to copy it to the
> result ?
> There is something else to pay attention for, the early return should
> come at least after the return array bounds have been set. In the
> testcase an array with the correct bounds has been allocated beforehand
> to hold the return value, but it’s not always the case.
you are absolutely right, I had gotten that wrong.
> For what it’s worth, the non-generic variant in pack.m4 (or in
> pack_{i,f,c}{1,2,4,8,10,16}.c) has a zero extent check and it clears the
> source ptr in that case, which makes it setup the return array and then
> jump to the vector copy at the end of the function.
>
The code is so similar (for good reason) that it makes sense to keep
it synchronous. I added code for 'zero_sized' array with the minor
difference that I made it boolean instead of integer.
I also extended the testcase so that it exercises PACK/pack_internal
a little, for argument 'vector' present as well as not. (There are
existing tests for intrinsic types, but not for the issue at hand).
Regtested again, and checked the testcase (against other compilers
and also with valgrind).
OK now?
Thanks,
Harald
Works better with patch attached...
Am 13.12.21 um 21:25 schrieb Harald Anlauf via Gcc-patches:
> Hi Mikael,
>
> Am 09.12.21 um 21:37 schrieb Mikael Morin:
>> Hello,
>>
>> On 09/12/2021 21:05, Harald Anlauf via Fortran wrote:
>>> Dear all,
>>>
>>> I had thought that we had fixed this in the past (see PR31001),
>>> but it did fail for me with all gcc versions I have tried (7-12)
>>> for a slightly more elaborate case as in the old testcase.
>>>
>>> The loop in pack_internal did try to access the first element of
>>> the array argument to PACK even if one (or more) extents were zero.
>>> This is not good.
>>>
>>> Solution: check the extents and return early. (We already do a
>>> related check for the vector argument if present).
>>
>> If there is a vector argument, aren’t we supposed to copy it to the
>> result ?
>> There is something else to pay attention for, the early return should
>> come at least after the return array bounds have been set. In the
>> testcase an array with the correct bounds has been allocated beforehand
>> to hold the return value, but it’s not always the case.
>
> you are absolutely right, I had gotten that wrong.
>
>> For what it’s worth, the non-generic variant in pack.m4 (or in
>> pack_{i,f,c}{1,2,4,8,10,16}.c) has a zero extent check and it clears the
>> source ptr in that case, which makes it setup the return array and then
>> jump to the vector copy at the end of the function.
>>
>
> The code is so similar (for good reason) that it makes sense to keep
> it synchronous. I added code for 'zero_sized' array with the minor
> difference that I made it boolean instead of integer.
>
> I also extended the testcase so that it exercises PACK/pack_internal
> a little, for argument 'vector' present as well as not. (There are
> existing tests for intrinsic types, but not for the issue at hand).
>
> Regtested again, and checked the testcase (against other compilers
> and also with valgrind).
>
> OK now?
>
> Thanks,
> Harald
>
Le 13/12/2021 à 21:27, Harald Anlauf via Fortran a écrit :
> Works better with patch attached...
>
> Am 13.12.21 um 21:25 schrieb Harald Anlauf via Gcc-patches:
>>
>> The code is so similar (for good reason) that it makes sense to keep
>> it synchronous. I added code for 'zero_sized' array with the minor
>> difference that I made it boolean instead of integer.
>>
>> I also extended the testcase so that it exercises PACK/pack_internal
>> a little, for argument 'vector' present as well as not. (There are
>> existing tests for intrinsic types, but not for the issue at hand).
>>
>> Regtested again, and checked the testcase (against other compilers
>> and also with valgrind).
>>
>> OK now?
>>
Yes, thanks.
From dfa1e1ac5d8e43f1ca8f13b64330825581174f36 Mon Sep 17 00:00:00 2001
From: Harald Anlauf <anlauf@gmx.de>
Date: Thu, 9 Dec 2021 20:55:08 +0100
Subject: [PATCH] Fortran: PACK intrinsic should not try to read from
zero-sized array
libgfortran/ChangeLog:
PR libfortran/103634
* intrinsics/pack_generic.c (pack_internal): Handle case when the
array argument of PACK has one extent of size zero to avoid
invalid reads.
gcc/testsuite/ChangeLog:
PR libfortran/103634
* gfortran.dg/zero_sized_13.f90: New test.
---
gcc/testsuite/gfortran.dg/zero_sized_13.f90 | 20 ++++++++++++++++++++
libgfortran/intrinsics/pack_generic.c | 4 ++++
2 files changed, 24 insertions(+)
create mode 100644 gcc/testsuite/gfortran.dg/zero_sized_13.f90
new file mode 100644
@@ -0,0 +1,20 @@
+! { dg-do run }
+! PR libfortran/103634 - Runtime crash with PACK on zero-sized arrays
+
+program p
+ implicit none
+ type t
+ real :: r(24) = -99.
+ end type
+ type(t), allocatable :: new(:), old(:)
+ logical, allocatable :: mask(:)
+ integer :: n, m
+! m = 1 ! works
+ m = 0 ! failed with SIGSEGV in pack_internal
+ allocate (old(m), mask(m))
+ mask(:) = .false.
+ n = count (mask)
+ allocate (new(n))
+ new(:) = pack (old, mask)
+ print *, size (new)
+end
@@ -126,6 +126,10 @@ pack_internal (gfc_array_char *ret, const gfc_array_char *array,
if (mstride[0] == 0)
mstride[0] = mask_kind;
+ for (n = 0; n < dim; n++)
+ if (extent[n] == 0)
+ return;
+
if (ret->base_addr == NULL || unlikely (compile_options.bounds_check))
{
/* Count the elements, either for allocating memory or
--
2.26.2