Bug 37199

Summary: array assignment from function writes out of bounds
Product: gcc Reporter: Gavin Salam <salam>
Component: fortranAssignee: Daniel Kraft <domob>
Status: RESOLVED FIXED    
Severity: normal CC: d, dfranke, gcc-bugs, salam, tkoenig
Priority: P3 Keywords: wrong-code
Version: 4.4.0   
Target Milestone: ---   
Host: Target:
Build: Known to work:
Known to fail: 4.4.0 4.2.0 4.3.0 Last reconfirmed: 2008-09-07 14:04:38
Bug Depends on:    
Bug Blocks: 32834    
Attachments: test case

Description Gavin Salam 2008-08-22 10:27:12 UTC
In the latest gfortran snapshot4.4.0 20080821 (but also in 4.2.3 and 4.3.0) on a 32-bit machine, the attached piece of code crashes on execution (e.g. compiled with -g -Wall), with error
  
  *** glibc detected *** a.out: free(): invalid pointer: 0x0805fb48 ***

Running with valgrind indicates

==16436== Invalid write of size 8
==16436==    at 0x8048944: MAIN__ (bounds_issue.f90:9)
==16436==    by 0x8048B36: main (in /...bounds_...

This suggests the array assignment is writing out of the bounds of the array.

The code executes without issue on all other f95 compilers tried out. This bug is a showstopper in using gfortran with the hoppet package, http://projects.hepforge.org/hoppet/

-------------------------------------------------------
gfortran -v

Using built-in specs.
Target: i586-pc-linux-gnu
Configured with: /home/fx/gfortran_nightbuild/trunk/configure --prefix=/home/fx/gfortran_nightbuild/irun-20080821 --enable-languages=c,fortran --build=i586-pc-linux-gnu --enable-checking=release --with-gmp=/home/fx/gfortran_nightbuild/software
Thread model: posix
gcc version 4.4.0 20080821 (experimental) [trunk revision 139368] (GCC)
Comment 1 Gavin Salam 2008-08-22 10:29:52 UTC
Created attachment 16127 [details]
test case

# to see the issue, type
gfortran -g -Wall bounds_issue.f90
./a.out
# or
valgrind ./a.out
Comment 2 Dominique d'Humieres 2008-08-22 10:56:31 UTC
Confirmed on i686-apple-darwin9 (both 32 and 64 bit modes). Compiling the test with -fbounds-check gives at run time:

At line 18 of file pr37199.f90
Fortran runtime error: Array bound mismatch for dimension 1 of array 'pxq'

The modified test

program bounds_issue
  implicit none
  integer, parameter  :: dp = kind(1.0d0)
  real(dp), pointer :: pdf0(:,:), dpdf(:,:)

  allocate(pdf0(0:282,-6:7))
  allocate(dpdf(0:282,-6:7))  ! with dpdf(0:283,-6:7) [illegal] error disappears
  write(*,*) lbound(dpdf), ubound(dpdf)
  pdf0 = 1.0d0
  dpdf = 0.0d0
  dpdf = tmp_PConv(pdf0)
  if (any(dpdf /= pdf0)) call abort()

contains
  function tmp_PConv(q_in) result(Pxq)
    real(dp),      intent(in) :: q_in(0:,-6:)
    real(dp)                  :: Pxq(0:ubound(q_in,dim=1),-6:7)
    Pxq = 0d0
    write(*,*) lbound(q_in), ubound(q_in)
    write(*,*) lbound(Pxq),  ubound(Pxq)
    Pxq = q_in
    return
  end function tmp_PConv

end program bounds_issue

gives at run time

           0          -6         282           7
           0          -6         282           7
           0          -6         282           7

and does not abort, suggesting that the copy has been properly done. It looks like as another "off by one" issue(?).

Comment 3 Daniel Franke 2008-08-22 13:25:16 UTC
Further reduced:

program bounds_issue
  real, pointer :: pdf0(:)
  allocate(pdf0(0:282))
  pdf0 = f(pdf0)

contains
  function f(x)
    real, intent(in) :: x(0:)                  ! x(1:), f(1:...) works
    real             :: f(0:ubound(x,dim=1))   ! x(-1:), f(-1:...) crashes
    f = 0.0
  end function
end program bounds_issue

If the lower bounds in the function argument and function return value are set to 1 and -1 respectively, then the diff of the respective tree dumps is quite small. Which of the seven changes which can be observed is the significant one, I can not tell.
Comment 4 Daniel Kraft 2008-09-07 14:46:10 UTC
     parm.12.dim[0].ubound = D.1541;
     parm.12.dim[0].stride = NON_LVALUE_EXPR <D.1546>;
     parm.12.data = (void *) &(*ifm.11)[0];
     parm.12.offset = NON_LVALUE_EXPR <D.1545>;
     D.1547 = MAX_EXPR <(parm.12.dim[0].ubound - parm.12.dim[0].lbound) + 1, 0>;
-    D.1548 = D.1547 + -1;
+    D.1548 = D.1547 + -2;
     atmp.13.dtype = 281;
     atmp.13.dim[0].stride = 1;
     atmp.13.dim[0].lbound = 0;
     atmp.13.dim[0].ubound = D.1548;

This one (from the diffs on my machine) is the problematic one.  The value subtracted should be -1 independently of the lower bound.  D.1548 will later be used in calculating the size of the temporary allocated, and for a lower-bound of 2, for instance, one element will be missing (for lower-bound 3 it would be 2 elements).

I'm not yet completely sure what the problem is for a lower bound of 0 or -1, though.  There, too much memory is allocated and the atmp's (the result array's) upper bound is too large.  But I'll try to locate the problem in gfortran now and fix it and see what this does for all kinds of bounds.
Comment 5 Daniel Kraft 2008-09-07 19:00:37 UTC
program bounds_issue
  real, pointer :: pdf0(:)
  allocate(pdf0(0:282))
  pdf0 = f(pdf0)

contains
  function f(x)
    real, intent(in) :: x(0:)                  ! x(1:), f(1:...) works
    real             :: f(0:ubound(x,dim=1))   ! x(-1:), f(-1:...) crashes
    f = 0.0
  end function
end program bounds_issue

---

For the temporary array holding the result and the loop assigning back to pdf0, the bounds of f(0:ubound(x,dim=1)) are used.  The interface mapping of x in the upper bound however does not supply an array-spec to the symbol, and thus UBOUND(x) returns the number of elements in x rather than the real upper bound matching the fixed lower-bound of 0 as for an entity not being a full array.

Thus, the loop starts at 0 because of the constant lower bound but runs one element too far because the upper bound is evaluated as if the lower was 1.  This leads to the segfault in any case but when 1 is indeed the lower bound.  It seems to be possible to simply copy the array-spec to new_sym in gfc_add_interface_mapping, I'm working this out at the moment.
Comment 6 Daniel Kraft 2008-09-08 09:18:50 UTC
Subject: Bug 37199

Author: domob
Date: Mon Sep  8 09:17:27 2008
New Revision: 140102

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=140102
Log:
2008-09-08  Daniel Kraft  <d@domob.eu>

	PR fortran/37199
	* trans-expr.c (gfc_add_interface_mapping): Set new_sym->as.
	(gfc_map_intrinsic_function): Added checks against NULL bounds in
	array specs.

2008-09-08  Daniel Kraft  <d@domob.eu>

	PR fortran/37199
	* gfortran.dg/array_function_2.f90: New test.

Added:
    trunk/gcc/testsuite/gfortran.dg/array_function_2.f90
Modified:
    trunk/gcc/fortran/ChangeLog
    trunk/gcc/fortran/trans-expr.c
    trunk/gcc/testsuite/ChangeLog

Comment 7 Gavin Salam 2008-09-10 13:24:19 UTC
That's great. I've tested it with the latest binary snapshot and it works very nicely.

Thanks a lot for fixing this!

Gavin
Comment 8 Daniel Kraft 2008-09-11 19:15:20 UTC
Subject: Bug 37199

Author: domob
Date: Thu Sep 11 19:13:59 2008
New Revision: 140296

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=140296
Log:
2008-09-08  Daniel Kraft  <d@domob.eu>

	PR fortran/37199
	* trans-expr.c (gfc_add_interface_mapping): Set new_sym->as.
	(gfc_map_intrinsic_function): Added checks against NULL bounds in
	array specs.

2008-09-08  Daniel Kraft  <d@domob.eu>

	PR fortran/37199
	* gfortran.dg/array_function_2.f90: New test.

Added:
    branches/gcc-4_3-branch/gcc/testsuite/gfortran.dg/array_function_2.f90
Modified:
    branches/gcc-4_3-branch/gcc/fortran/ChangeLog
    branches/gcc-4_3-branch/gcc/fortran/trans-expr.c
    branches/gcc-4_3-branch/gcc/testsuite/ChangeLog

Comment 9 Daniel Kraft 2008-09-11 19:15:52 UTC
Fixed on 4.3 branch.