A recent report from the White House Office of the National Cyber Director highlights the need for “Memory-Safe” computer languages and tools. Cybersecurity is a major driver for this initiative, and it’s undeniable that innumerable malicious exploits have targeted dangling pointers and buffer over-runs to steal data or subvert critical systems. However, productivity also suffers badly when memory errors cause hang-ups, blue-screens, and other erratic and non-reproducible behaviours. Bugs like this are deeply frustrating for end-users, and notoriously difficult to debug.
Fortran is not immune to these problems, though it is less vulnerable than pointer-based languages. Fortran does have a restricted form of pointers, but for most applications, allocatable arrays provide a flexible and memory safe alternative. Unlike pointer arrays, allocatable arrays cannot leak memory or leave dangling references.
The table below shows how some current compilers deal with common memory-safety errors. It shows that Fortran can be memory-safe, but that compilers that excel for execution speed may trail in diagnostics, and vice versa.
Source | Run-time Error | Silverfrost | gfortran | Intel IFX | nvFortran | NAG |
ftn95 9.0 | 12.3.0 | 2024.0.2 | 24.3 | 7.1 | ||
Percentage Passes1 | 94% | 48% | 54% | 35% | 96% | |
AERMOD.f90 execution time with diagnostic switches (seconds)2 | 86 | 20 | 29 | 19 | 127 | |
AERMOD.f90 execution time optimized (seconds)2 | 11 | 10 | 3 | 5 | 8 | |
Can mix checked and unchecked code | Yes | Yes | Yes | Yes | No | |
Full trace-back from run-time errors | Yes | No | Yes | Yes | Yes | |
ARG1 | Argument mismatch – same file | Yes5 | Yes4 | Yes4 | No | Yes4 |
ARG2 | Argument mismatch – different file6 | Yes5 | No | Yes4,8 | No | Yes5 |
ARG3 | Illegal assignment to constant argument – no INTENT specified | Yes5 | No | Yes5,9 | Yes5,9 | Yes5 |
ARG4 | Illegal assignment to constant argument – INTENT specified | Yes4 | Yes4 | Yes4 | Yes3,5,9 | Yes4 |
ARG5 | Illegal assignment to DO loop variable in SUBROUTINE – no INTENT specified | Yes5 | Yes5 | No | No | Yes5 |
ARG6 | Illegal assignment to DO loop variable in SUBROUTINE – INTENT specified | Yes4 | Yes4 | Yes4 | Yes3 | Yes4 |
ARG7 | Scalar constant passed to array dummy argument | Yes5 | Yes4 | Yes4 | No | Yes4 |
OARG1 | Illegal use of optional argument | Yes5 | No | Yes5,9 | Yes5,9 | Yes5 |
ALIAS1 | Aliased dummy argument variable | Yes5 | No | No | No | Yes5 |
ALIAS2 | Aliased dummy argument array | Yes5 | No | No | No | No |
Source | Run-time Error | Silverfrost | gfortran | Intel IFX | nvFortran | NAG |
BND1 | Array bound error – X(100) | Yes5 | Yes3,5 | Yes5 | Yes5 | Yes5 |
BND2 | Array bound error – X(N) N is argument | Yes5 | Yes5 | Yes5 | Yes5 | Yes5 |
BND3 | Array bound error – X(N) N is in COMMON | Yes5 | Yes5 | Yes5 | Yes5 | Yes5 |
BND4 | Array bound error – X(N) N is in MODULE | Yes5 | Yes5 | Yes5 | Yes5 | Yes5 |
BND5 | Assumed size array bound error – X(*) | Yes5 | No | No | No | Yes5 |
BND6 | Array bound error – X(M:N) M and N argument – lower bound violated | Yes5 | Yes5 | Yes5 | Yes5 | Yes5 |
BND7 | Array bound error – automatic array | Yes5 | Yes5 | Yes5 | Yes5 | Yes5 |
BND8 | Array bound error – allocatable array | Yes5 | Yes5 | Yes5 | Yes5 | Yes5 |
BND9 | Multi-dimensional array bound error within overall array bounds | Yes5 | Yes5 | Yes5 | Yes5 | Yes5 |
BND10 | Array bound error – assign to dummy argument which is larger than actual argument | Yes5 | No | No | No | Yes5 |
BND11 | Array bound error – pointer array component of derived type | Yes5 | Yes5 | Yes5 | Yes5 | Yes5 |
CBND1 | Character bound error – local | Yes5 | Yes5 | Yes5 | No | Yes5 |
CBND2 | Character bound error – COMMON | Yes5 | Yes5 | Yes5 | No | Yes5 |
CBND3 | Character bound error – assign to dummy which is larger than actual argument | Yes5 | Yes3,5 | Yes4 | No | Yes4 |
CBND4 | Character bound error – CHARACTER*(*) | Yes5 | No | Yes5 | No | Yes5 |
Source | Run-time Error | Silverfrost | gfortran | Intel IFX | nvFortran | NAG |
UIN1 | Uninitialized variable7 – local | Yes3,5 | Yes3 | No | No | Yes3,5 |
UIN2 | Uninitialized variable – argument | Yes5 | No | No | No | Yes5 |
UIN3 | Uninitialized variable – COMMON | Yes5 | No | No | No | Yes5 |
UIN4 | Uninitialized variable – MODULE | Yes5 | No | No | No | Yes5 |
UIN5 | Uninitialized array element – local | Yes5 | No3 | No | No | Yes5 |
UIN6 | Uninitialized array element – argument | Yes5 | No | No | No | Yes5 |
UIN7 | Uninitialized array element – COMMON | Yes5 | No | No | No | Yes5 |
UIN8 | Uninitialized array element – MODULE | Yes5 | No | No | No | Yes5 |
UIN9 | Uninitialized array element – local array in SUBROUTINE | Yes5 | No | No | No | Yes5 |
UIN10 | Uninitialized array element – automatic arrays | Yes5 | No | No | No | Yes5 |
UIN11 | Uninitialized array element – allocatable arrays | Yes5 | No | No | No | Yes5 |
UIN12 | Uninitialized array element – saved arrays | Yes5 | No | No | No | Yes5 |
UIN13 | Uninitialized array element – INTENT(OUT) arrays | Yes5 | No | No | No | Yes5 |
UIN14 | Two byte array elements | No10 | No | No | No | Yes5 |
UIN15 | One byte array elements | No10 | No | No | No | Yes5 |
Source | Run-time Error | Silverfrost | gfortran | Intel IFX | nvFortran | NAG |
DO1 | Zero increment DO loop | Yes5 | Yes5 | Yes5,9 | Yes5,9 | Yes5 |
DO2 | Illegal assignment to local DO loop variable | Yes4 | Yes4 | Yes4 | Yes3 | Yes4 |
DO3 | Illegal assignment to local DO loop variable via EQUIVALENCE | Yes3,5 | Yes5 | No | No | Yes4 |
DO4 | Illegal assignment to DO variables in CONTAINed subprogram | Yes5 | Yes4 | No | No | Yes5 |
SF1 | SUBROUTINE referenced as a FUNCTION – same file | Yes5 | Yes4 | Yes4 | Yes4 | Yes4 |
SF2 | SUBROUTINE referenced as a FUNCTION – different file6 | Yes5 | No | Yes5,9 | No | Yes5 |
FMT1 | Illegal run-time format | Yes5 | Yes5 | Yes5 | Yes5 | Yes5 |
CONF | Non-conformant array assignment | Yes5 | Yes5 | Yes5 | No | Yes5 |
PTR1 | Assign via pointer after target deallocated | Yes5 | No | No | No | Yes5 |
PTR2 | Assign via global pointer to local array after subprogram return | Yes5 | No | Yes5,9 | No | Yes5 |
IOFL | Integer overflow | Yes5 | No | No | No | Yes5 |
LEAK | Garbage collection | No | No | No | No | Yes11 |
Compiler Switches | |
Silverfrost FTN95 9.0 | ftn95 /full_undef /check_alias |
gfortran 12.3.0 | gfortran -fcheck=all -Wall -Waliasing -pedantic -fbacktrace -g |
Intel IFX 2024.0.2 | ifx /check:all /fpe:0 /traceback /warn:all,nodec,noexternals /auto /Od /gen-interfaces /warn-interfaces /Qtrapuv /Qansi-alias /link /stack:1000000 |
NAG 7.1 | f95 -C=all -C=undefined -info |
nvFortran 24.3 | nvfortran -g -Mbounds -Mchkstk -Mchkptr -Minform=inform -C -traceback |
Notes | |
Thanks to Herman D. Knoble and Arnaud Desitter for supplying some of the diagnostic tests. | |
All tests were run using Windows 11 on an ASUS laptop with AMD Ryzen 7 6800U processor and 16GB RAM. In the case of nvfortran, the tests were run using WSL (Windows Subsystem for Linux).. | |
1 | “Percentage Passes” is calculated from a total of 54 tests. |
2 | Execution Time for AERMOD.f90 benchmark, compiled using the diagnostic switches (see table above) or with somewhat optimized settings (indicative only). |
3 | Compiler issued warning message. |
4 | Compiler issued fatal error message – no executable produced. |
5 | Run-time message. |
6 | Arranged so that error is not visible at compile time. |
7 | A variable is “uninitialized” if it has never been assigned a value. Data elements should be initialized before their value is used. The illegal use of an uninitialized variable can sometimes be detected at compile-time, but, because it may be data and flow dependent, usually requires run-time monitoring. In some cases, compilers implement this by checking for a special “uninitialized” bit pattern, such as 0x80, but this may result in false positives for one or two byte variables (and, rarely, in other cases). Use of a “shadow array” doubles memory requirements, but avoids false positives. |
8 | Compilation order dependent. Compiler generates .mod file to allow interface checking. |
9 | Non-specific error message, but full traceback. |
10 | False positive was reported at run time. |
11 | Fortran programs that use pointers to allocate arrays and structures can leak memory, unless the programmer takes care to ensure the memory is freed before it becomes inaccessible.. An automated garbage collector may retrieve leaked memory. The Linux NAG Fortran compiler has a garbage collection option, but we have not tested it. |