Is Fortran “Memory Safe”?

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.

Download source code of tests

Compiler Comparisons (last updated 2017)