Bug 109861 - Optimization is marking uninitialized C_PTR being passed to a C function, causes segfault.
Summary: Optimization is marking uninitialized C_PTR being passed to a C function, c...
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: fortran (show other bugs)
Version: 13.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-05-15 14:00 UTC by Scot Breitenfeld
Modified: 2023-05-16 13:29 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2023-05-15 00:00:00


Attachments
Fortran/C/Makfile. Changing the optimization level in the makefile reproduces the behaviour. (775 bytes, application/x-tar)
2023-05-15 14:00 UTC, Scot Breitenfeld
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Scot Breitenfeld 2023-05-15 14:00:14 UTC
Created attachment 55085 [details]
Fortran/C/Makfile. Changing the optimization level in the makefile reproduces the behaviour.

For a TYPE(C_PTR), INTENT(OUT), buf, which is being passed through a Fortran subroutine to get the value set in a C function, an optimization of >= -O1 gives the warning "used uninitialized [-Wuninitialized]" and then the program Segmentation faults - invalid memory reference. It works for gfortran 12 but fails for gfortran 13 and 14. It works if the INTENT is INOUT.
Comment 1 anlauf 2023-05-15 18:23:29 UTC
I believe the sample code is misleading, and the behavior is expected:

  SUBROUTINE h5aread_async_f(buf)
    TYPE(C_PTR), INTENT(OUT) :: buf

You can see what happens if you specify the flag -fdump-tree-original as
part of F90FLAGS.

Now compare the dump-tree for INTENT(INOUT) vs. INTENT(OUT):

--- fcode.F90.005t.original.inout       2023-05-15 20:03:07.292148948 +0200
+++ fcode.F90.005t.original.out 2023-05-15 20:03:36.292208016 +0200
@@ -19,6 +19,7 @@
     D.4223 = (void *) &attr_rdata0;
     f_ptr = D.4223;
   }
+  f_ptr = {CLOBBER};
   h5aread_async_f (&f_ptr);
   {
     struct __st_parameter_dt dt_parm.0;

When the dummy argument buf is declared with INTENT(OUT), we mark the
actual argument in the caller with CLOBBER, which means that the optimizer
may throw away previous calculations and assignments as they do not matter.

If you add a line

    print '(Z16.16)', buf

into that subroutine, you'll see that the clobber annotation serves its
purpose once optimization is enabled.

I think that you might want to cross-check your testcase with the NAG
compiler, or some other compiler which provides a means to initialize
INTENT(OUT) arguments to detect such code.
Comment 2 anlauf 2023-05-15 18:49:50 UTC
(In reply to anlauf from comment #1)
> I think that you might want to cross-check your testcase with the NAG
> compiler, or some other compiler which provides a means to initialize
> INTENT(OUT) arguments to detect such code.

I've just checked: NAG behaves similarly to gfortran; the code crashes
with INTENT(OUT) and works with INTENT(INOUT) as well as INTENT(IN).
Comment 3 Scot Breitenfeld 2023-05-15 20:08:40 UTC
I see the same issue with NAG, regardless of the optimization level. Our CI testing had missed it because this was a parallel test, and we don't test parallel with NAG.

I guess the issue is whether marking TYPE(C_PTR) as CLOBBER is correct. I looked through the 2018 standard and could not locate anything that addresses this use case. Are you interpreting the possibility that a TYPE(C_PTR) should not be declared INTENT(OUT)?

I can instead change the subroutine to declare buf as

INTEGER(C_INT), INTENT(OUT), TARGET :: buf

and f_ptr = C_LOC(buf) and there is no issue. So it seems to depend on the TYPE of the argument being passed.
Comment 4 anlauf 2023-05-15 21:22:28 UTC
(In reply to Scot Breitenfeld from comment #3)
> I guess the issue is whether marking TYPE(C_PTR) as CLOBBER is correct. I
> looked through the 2018 standard and could not locate anything that
> addresses this use case. Are you interpreting the possibility that a
> TYPE(C_PTR) should not be declared INTENT(OUT)?

Maybe I am missing your intention, but I interpret your code that you
want to pass a (C) pointer to variable attr_rdata0 to return your result.
But that needs to be intent(in).  Your subroutine is not really supposed
to return a pointer but a result in the location the pointer dereferences.

Feel free to correct me.

> I can instead change the subroutine to declare buf as
> 
> INTEGER(C_INT), INTENT(OUT), TARGET :: buf
> 
> and f_ptr = C_LOC(buf) and there is no issue.

I cannot confirm this with my gcc installations, and there is no reason
that this should make a difference.

> So it seems to depend on the
> TYPE of the argument being passed.

There are cases where no clobber is currently generated.  For example,
if the dummy variable is a Fortran pointer, which has a completely
different semantics from TYPE(C_PTR).

Still I don't understand why you don't use INTENT(IN) for the pointer.
In that case you could do things in the main like:

  CALL H5Aread_async_f(C_LOC(attr_rdata0))

which appears to represent what I am guessing, and which gets rejected for
INTENT /= IN with a possibly more helpful error message.
Comment 5 kargls 2023-05-15 21:25:28 UTC
(In reply to Scot Breitenfeld from comment #3)
> I see the same issue with NAG, regardless of the optimization level. Our CI
> testing had missed it because this was a parallel test, and we don't test
> parallel with NAG.
> 
> I guess the issue is whether marking TYPE(C_PTR) as CLOBBER is correct. I
> looked through the 2018 standard and could not locate anything that
> addresses this use case. Are you interpreting the possibility that a
> TYPE(C_PTR) should not be declared INTENT(OUT)?

Fortran 2023, 8.5.10

The INTENT (OUT) attribute for a nonpointer dummy argument specifies
that the dummy argument becomes undefined on invocation of the procedure,
except for any subcomponents that are default-initialized (7.5.4.6).
Comment 6 Scot Breitenfeld 2023-05-16 13:27:21 UTC
Thanks for the standard reference. That is indeed what I was looking for. I understand now that, in this case, the INTENT refers to whether C_PTR can be changed and does not relate to the INTENT state of the target. I should change all my usage cases to INTENT(IN) since none of the Fortran wrappers allow changes to the pointer itself.