Why no runtime error when clearly writing over array bounds?
Asked Answered
I

2

6

I have a program that assigns an array beyond it's bounds, and I was expecting a run-time error to be thrown. Yet no error is raised at all and the program proceeds to write into undeclared memory. Is there some compiler option to guard against this? With the memory dump shown it is clear this overreach of bounds is real. Is there a way to declare variables or argument specs to catch this? Obviously this is a clear case, but when tasked to maintain thousands of lines of F77 derived code it is not always clear (to me) if this might be happening.

PROGRAM TEST_CODE
IMPLICIT NONE

INTEGER*4 :: R(5)           ! Array of 5

    CALL R_TEST(R, 10)

END PROGRAM

SUBROUTINE R_TEST(R, J)
IMPLICIT NONE

INTEGER*4, INTENT(INOUT) :: R(1)    ! Dummy is array of 1
INTEGER*4, INTENT(IN) :: J
INTEGER*4 :: K

DO K=J-5,J+5            ! K=5..15
    R(K) = K            ! No Runtime Error
END DO

END SUBROUTINE

Compiler is Intel Fortran 2011 XE, and yes I am using the byte spec INTEGER*4 because I know what I get with it.

Here are the compiler options for runtime checking. Compiler Options

Memory of <code>R</code> variable

Insincerity answered 23/3, 2012 at 14:12 Comment(0)
S
5

The intel compiler does a very good job on bounds checking for the pointer and allocatable arrays. If you slightly modify your code (below) and compile with something like:

$ ifort -O0 -debug -traceback -check -ftrapuv TEST_CODE.f90

you will get a run time error. But for assumed size arrays the intel compiler cannot check the bounds. Especially for F77 codes with implicit typing and so on it won't be easy to find memory leaks. Another tiny thing, in Fortran your program has to do something meaningful; otherwise the compiler will omit your code because it simply does nothing! That's why I added a print in the end.

There is a small problem with R(:) thing is that the compiler cannot assume that it is contiguous in the memory; hence it cannot make some compiler optimization. It would then be better to use allocatable arrays or use the contiguous attribute (F2008 standard).

PROGRAM TEST_CODE
IMPLICIT NONE

INTEGER*4 :: R(5)           ! Array of 5

    CALL R_TEST(R, 10)
    print *,R

END PROGRAM

SUBROUTINE R_TEST(R, J)
IMPLICIT NONE

INTEGER*4, INTENT(INOUT) :: R(:)    ! Dummy is array of 1
INTEGER*4, INTENT(IN) :: J
INTEGER*4 :: K

DO K=J-5,J+5            ! K=5..15
    R(K) = K            ! No Runtime Error
END DO

END SUBROUTINE
Scratches answered 27/3, 2012 at 0:13 Comment(2)
Thanks for the answer. If the compiler cannot assume contiguous memory how does it handle array indexing. Isn't each array increment a fixed distance away from the previous one, like with C arrays?Insincerity
No, the assumed shape arrays like R(:) are not guaranteed to be continuous. C does not have arrays (pointers are not counted!). Though, there are methods in C to create contiguous array pointers (see #5196818 ). In Fortran, you can test this simply. I tested and I get 10-15% performance increase when I use allocatable attribute. When you do mixed C/Fortran programming, you can map R(:) to a C type pointer array.Scratches
A
4

Interesting. gfortran 4.6 finds the runtime subscript error:

At line 18 of file test_code.f90
Fortran runtime error: Index '5' of dimension 1 of array 'r' above upper bound of 1 

but ifort XE 12.1.1.246 doesn't.

EDIT: here is the answer from the Intel compiler documentation: "For arrays that are dummy arguments, only the lower bound is checked for a dimension whose upper bound is specified as * or where the upper and lower bounds are both 1." And when the declaration is changed to R(2) ifort also finds the subscript mistake.

The reason for this is that much old code used a value of "1" for the size of a dummy argument array to indicate an unknown size. This works if you just regard the argument as an address but of course makes any subscript checking impossible because the compiler doesn't know the size of the dummy argument. This technique should not be used in new code. Fortran 90 provides better options, e.g., assumed-shape arrays (colon declaration).

So the answer to "not always clear (to me) if this might be happening", i.e, when your legacy code isn't being checked by ifort -- looked for procedure arguments declared as (1) or (*) or the same for one or more of multiple dimensions.

Algol answered 23/3, 2012 at 14:32 Comment(4)
How do you force gfortran to do ranfe checks? I always have to use valgrind or Solaris Studio. I looked into debugging options many times.Nappe
The debugging compiler options for gfortran that I use are, in Unix/Mac style: -O2 -fimplicit-none -Wall -Wline-truncation -Wcharacter-truncation -Wsurprising -Waliasing -Wimplicit-interface -Wunused-parameter -fwhole-file -fcheck=all -std=f2008 -pedantic -fbacktrace. These may generate a lot of warning with old code so you may need to remove some.Algol
ranfe is probably range. gfortran -fcheck=all contains -fcheck=bounds.Algol
Thanks, interesting, I use -fbacktrace -ggdb -pedantic -Wall -fcheck=all -ffpe-trap=zero,overflow and it works on simple test case I now tried, but I really recall it didn't work with my larger scale program.Nappe

© 2022 - 2024 — McMap. All rights reserved.