Bug 82996 - ICE and segfault with derived type finalization
Summary: ICE and segfault with derived type finalization
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: fortran (show other bugs)
Version: 6.4.1
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks: Finalization
  Show dependency treegraph
 
Reported: 2017-11-14 20:23 UTC by Neil Carlson
Modified: 2019-01-25 23:19 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2017-11-15 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Neil Carlson 2017-11-14 20:23:38 UTC
I'm going to give 3 examples. The first gives a spurious run time segfault. The others are attempts to work around the problem, but give an internal compiler error.  These all work fine with the Intel and NAG compilers.

The first example:

module mod

  type foo
    integer, pointer :: f(:) => null()
  contains
    final :: foo_destroy
  end type
  
  type bar
    type(foo) :: b(2)
  end type

contains

  elemental subroutine foo_destroy(this)
    type(foo), intent(inout) :: this
    if (associated(this%f)) deallocate(this%f)
  end subroutine

end module

program main

  use mod
  type(bar) :: x
  call sub(x)
  
contains

  subroutine sub(x)
    type(bar), intent(out) :: x
  end subroutine

end program

And the output from running the executable:

$ gfortran -g gfortran-bug-20171114a.f90 
$ ./a.out

Program received signal SIGSEGV: Segmentation fault - invalid memory reference.

Backtrace for this error:
#0  0x7f1188b42df7 in ???
#1  0x7f1188b4202d in ???
#2  0x7f118803694f in ???
#3  0x400fa7 in __mod_MOD_foo_destroy
	at /home/nnc/Fortran/Bugs/gfortran/tmp/gfortran-bug-20171114a.f90:46
#4  0x400f0f in __mod_MOD___final_mod_Foo
	at /home/nnc/Fortran/Bugs/gfortran/tmp/gfortran-bug-20171114a.f90:49
#5  0x400b29 in __mod_MOD___final_mod_Bar
	at /home/nnc/Fortran/Bugs/gfortran/tmp/gfortran-bug-20171114a.f90:49
#6  0x401026 in sub
	at /home/nnc/Fortran/Bugs/gfortran/tmp/gfortran-bug-20171114a.f90:59
#7  0x40104a in MAIN__
	at /home/nnc/Fortran/Bugs/gfortran/tmp/gfortran-bug-20171114a.f90:55
#8  0x401080 in main
	at /home/nnc/Fortran/Bugs/gfortran/tmp/gfortran-bug-20171114a.f90:53
Segmentation fault (core dumped)
Comment 1 Neil Carlson 2017-11-14 20:27:11 UTC
In the second example, I add a final procedure for BAR (not necessary) and explicitly call the FOO final procedure on its B component.  This gives an ICE

f951: internal compiler error: in generate_finalization_wrapper, at fortran/class.c:1975

module mod

  type foo
    integer, pointer :: f(:) => null()
  contains
    final :: foo_destroy
  end type
  
  type bar
    type(foo) :: b(2)
  contains
    final :: bar_destroy
  end type

contains

  elemental subroutine foo_destroy(this)
    type(foo), intent(inout) :: this
    if (associated(this%f)) deallocate(this%f)
  end subroutine
  
  subroutine bar_destroy(this)
    type(bar), intent(inout) :: this
    call foo_destroy(this%b)
  end subroutine

end module

program main
  use mod
  type(bar) :: x
  call sub(x)
contains
  subroutine sub(x)
    type(bar), intent(out) :: x
  end subroutine
end program
Comment 2 Neil Carlson 2017-11-14 20:30:05 UTC
In the final example I drop the elemental attribute from the FOO final procedure and modify the BAR final procedure to loop over the elements of its B array component.  This too yields an ICE:

f951: internal compiler error: in generate_finalization_wrapper, at fortran/class.c:1975

module mod

  type foo
    integer, pointer :: f(:) => null()
  contains
    final :: foo_destroy
  end type
  
  type bar
    type(foo) :: b(2)
  contains
    final :: bar_destroy
  end type

contains

  subroutine foo_destroy(this)
    type(foo), intent(inout) :: this
    if (associated(this%f)) deallocate(this%f)
  end subroutine
  
  subroutine bar_destroy(this)
    type(bar), intent(inout) :: this
    integer :: j
    do j = 1, size(this%b)
      call foo_destroy(this%b(j))
    end do
  end subroutine

end module

program main
  use mod
  type(bar) :: x
  call sub(x)
contains
  subroutine sub(x)
    type(bar), intent(out) :: x
  end subroutine
end program
Comment 3 Martin Liška 2017-11-15 06:48:58 UTC
Confirmed.
Comment 4 Dominique d'Humieres 2017-11-15 09:45:51 UTC
I see the problems on all gfortran supporting finalization (4.9 up to trunk 8.0), except the ICEs that are no longer present on recent trunk. The change occurred between revisions r247817 (2017-05-09, ICE) and r248367 (2017-05-23, compiles). In top of that the code in comment 2 executes without segfault.
Comment 5 Neil Carlson 2017-11-15 22:14:47 UTC
I've built the svn trunk and tested the examples with it.  The ICEs with the comment 2 and 3 examples are gone, as Dominique found.  The comment 1 example continues to segfault when executed, as does the comment 2 example now.  The comment 3 example executes without error.  I think Dominique swapped 2 and 3.
Comment 6 Ondřej Čertík 2017-11-16 21:48:41 UTC
The finalizers are the most serious problem with gfortran for us. Every other bug we can workaround one way or another it seems, but the finalizers are very hard to workaround, one essentially has to comment them out, not just in our code, but also in all dependencies, and even then that introduces memory leaks.

What exactly is the problem? Is this a bug in the gfortran frontend, or something more fundamental? Is this a relatively simple fix for somebody who understands the internals, or would this require a significant time investment and redesign of the code?
Comment 7 Dominique d'Humieres 2017-11-16 23:33:35 UTC
> I think Dominique swapped 2 and 3.

Indeed!

If I compile the tests in comment 0 or 1 with '-fsanitize=address,undefined', I get at run time an error of the kind:

pr82996.f90:17: runtime error: member access within misaligned address 0x1000e3e7620c for type 'struct foo', which requires 8 byte alignment
0x1000e47d2bec: note: pointer points here
ASAN:DEADLYSIGNAL
=================================================================
==3427==ERROR: AddressSanitizer: SEGV on unknown address 0x12001c8fa57d (pc 0x00010796507b bp 0x7ffee84cd9c0 sp 0x7ffee84cd150 T0)
    #0 0x10796507a in wrap_write.part.20 (/opt/gcc/gcc7wr/lib/libasan.4.dylib+0x2507a)
    #1 0x109224d2e in __sanitizer::IsAccessibleMemoryRange(unsigned long, unsigned long) (/opt/gcc/gcc7wr/lib/libubsan.0.dylib+0x17d2e)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/opt/gcc/gcc7wr/lib/libasan.4.dylib+0x2507a) in wrap_write.part.20
==3427==ABORTING

Program received signal SIGABRT: Process abort signal.


If I remove 'elemental' for 'subroutine foo_destroy', the segfault is gone.
Comment 8 Neil Carlson 2017-11-16 23:38:48 UTC
> If I remove 'elemental' for 'subroutine foo_destroy', the segfault is gone.

In that case the final procedure doesn't match the array component and wouldn't be called.  I suspect that is why the segfault is gone.
Comment 9 Neil Carlson 2018-02-17 18:01:57 UTC
With today's version (r257782) I'm still seeing the same thing Dominique reported in comment 7, except that there is no longer any abort -- the programs terminate successfully (0 exit code) despite the reported runtime error.  I'm not sure what to make of that.

Example error:

 $ ./a.out
gfortran-20171114a.f90:48: runtime error: member access within misaligned address 0x00000060ab25 for type 'struct foo', which requires 8 byte alignment
0x00000060ab25: note: pointer points here
 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00
             ^
Comment 10 Neil Carlson 2018-09-28 14:11:36 UTC
A reader on c.l.f suggested this workaround for the bug. I'm sharing it here because I think it may help to isolate where the problem is.  The suggestion was to make the B array component allocatable and allocate it inside SUB. This allows more control over when its finalizer is called. Here's a modified version the runs without error (with -fsanitize=address,undefined) and valgrind shows nothing amiss. (I'm using the 9.0 trunk)

module mod

  type foo
    integer, pointer :: f(:) => null()
  contains
    final :: foo_destroy
  end type
  
  type bar
    type(foo), allocatable :: b(:)
  end type

contains

  elemental subroutine foo_destroy(this)
    type(foo), intent(inout) :: this
    if (associated(this%f)) deallocate(this%f)
  end subroutine

end module

program main

  use mod
  type(bar) :: x
  call sub(x) ! x%b not allocated
  call sub(x) ! x%b is allocated
  
contains

  subroutine sub(x)
    type(bar), intent(out) :: x
    allocate(x%b(2))
  end subroutine

end program

The interesting thing is that the finalizer works just fine when the %B component is allocatable and allocated (the second call to SUB), but not when it is not allocatable.