Skip to content

Commit b5fe08c

Browse files
committed
PWR089: Add entry and code examples
1 parent e9bbbb1 commit b5fe08c

6 files changed

Lines changed: 236 additions & 0 deletions

File tree

Checks/PWR089/README.md

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# PWR089: Remove unexpected arguments from procedure calls
2+
3+
### Issue
4+
5+
Calling a procedure while supplying excess arguments (i.e., more actual
6+
arguments than the procedure has dummy arguments) can lead to compilation
7+
failures or, if not caught, undefined behavior at runtime.
8+
9+
### Actions
10+
11+
Ensure the number of actual arguments supplied at the call site matches the
12+
number of dummy arguments in the procedure. If additional information is
13+
needed, adapt the procedure accordingly; for example, by adding new dummy
14+
arguments and handling them in the implementation.
15+
16+
### Relevance
17+
18+
When calling a Fortran procedure, the caller is expected to match the
19+
procedure's dummy argument list. If an explicit procedure interface is
20+
available at the call site, compilers can typically detect and report a
21+
mismatch in the number of supplied arguments during compilation.
22+
23+
However, Fortran also allows calling procedures without information about the
24+
expected arguments; in such cases, an _implicit interface_ is used. The caller
25+
effectively passes a list of memory addresses, which the called procedure
26+
interprets according to its dummy argument declarations. If the actual
27+
arguments exceed the dummy arguments, the program is exposed to undefined
28+
behavior.
29+
30+
A common misconception is that excess arguments are simply ignored. This may
31+
appear to work for some procedures, but it is not guaranteed. More
32+
specifically, some Fortran features can cause the compiler to supply hidden
33+
values to the called procedure; for example, character lengths, or array
34+
descriptors. If excess arguments are provided, those hidden values can be
35+
misinterpreted, resulting in incorrect results, "random" behavior, or even
36+
crashes.
37+
38+
> [!TIP]
39+
> To enhance code safety and reliability, ensure procedures provide
40+
> explicit interfaces to their callers. Check the [PWR068 entry](../PWR068/)
41+
> for more details!
42+
43+
### Code examples
44+
45+
The following example logs diagnostics for a simulation data field. The called
46+
procedure expects three arguments, but the caller accidentally passes an
47+
additional verbosity argument:
48+
49+
```fortran showLineNumbers
50+
! io.f90
51+
subroutine logField(name, step, value)
52+
use iso_fortran_env, only: real32
53+
implicit none
54+
55+
character(*), intent(in) :: name
56+
integer, intent(in) :: step
57+
real(kind=real32), intent(in) :: value
58+
59+
write(*, *) trim(name), " step=", step, " value=", value
60+
end subroutine logField
61+
```
62+
63+
```fortran {6,10,17} showLineNumbers
64+
! example.f90
65+
program call_with_excess_arguments
66+
use iso_fortran_env, only: real32
67+
implicit none
68+
69+
external :: logField
70+
character(len=20) :: name
71+
integer :: step
72+
real(kind=real32) :: val
73+
logical :: verbose
74+
75+
name = "field"
76+
step = 10
77+
val = 3.14
78+
verbose = .true.
79+
80+
call logField(name, step, val, verbose)
81+
end program call_with_excess_arguments
82+
```
83+
84+
Because `logField()` is an `external` procedure, the call uses an implicit
85+
interface. Compilers will typically allow this program to compile despite the
86+
argument count mismatch, which can corrupt the call frame if hidden information
87+
is passed for the `character` argument. In fact, a crash is triggered at
88+
runtime:
89+
90+
```txt
91+
$ gfortran --version
92+
GNU Fortran (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
93+
$ gfortran io.f90 example.f90
94+
$ ./a.out
95+
96+
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
97+
98+
Backtrace for this error:
99+
#0 0x76abe9223e59 in ???
100+
#1 0x76abe9222e75 in ???
101+
#2 0x76abe8e4532f in ???
102+
at ./signal/../sysdeps/unix/sysv/linux/x86_64/libc_sigaction.c:0
103+
#3 0x76abe94d20f9 in ???
104+
#4 0x76abe94d2194 in ???
105+
#5 0x63e416093238 in ???
106+
#6 0x63e416093359 in ???
107+
#7 0x63e416093395 in ???
108+
#8 0x76abe8e2a1c9 in __libc_start_call_main
109+
at ../sysdeps/nptl/libc_start_call_main.h:58
110+
#9 0x76abe8e2a28a in __libc_start_main_impl
111+
at ../csu/libc-start.c:360
112+
#10 0x63e4160930f4 in ???
113+
#11 0xffffffffffffffff in ???
114+
```
115+
116+
A simple solution is to remove the excess `verbose` argument from the procedure
117+
call:
118+
119+
```fortran {15} showLineNumbers
120+
! solution.f90
121+
program correct_call
122+
use iso_fortran_env, only: real32
123+
implicit none
124+
125+
external :: logField
126+
character(len=20) :: name
127+
integer :: step
128+
real(kind=real32) :: val
129+
130+
name = "field"
131+
step = 10
132+
val = 3.14
133+
134+
call logField(name, step, val)
135+
end program correct_call
136+
```
137+
138+
Now, the program will behave correctly:
139+
140+
```txt
141+
$ gfortran io.f90 solution.f90
142+
$ ./a.out
143+
field step= 10 value= 3.14000010
144+
```
145+
146+
Ultimately, it is recommended to encapsulate all procedures within modules so
147+
that callers have explicit interfaces. This enables the compiler to verify the
148+
provided arguments against the actual dummy arguments, preventing
149+
difficult-to-diagnose runtime bugs.
150+
151+
> [!TIP]
152+
> Check the [PWR068 entry](../PWR068/) for more details on implicit and
153+
> explicit interfaces!
154+
155+
### Related resources
156+
157+
- [PWR089
158+
examples](https://github.com/codee-com/open-catalog/tree/main/Checks/PWR089/)
159+
160+
### References
161+
162+
- ["Implicit
163+
Interfaces"](https://people.cs.vt.edu/~asandu/Courses/MTU/CS2911/fortran_notes/node44.html),
164+
Adrian Sandu. [last checked February 2026]
165+
166+
- ["More on Implicit
167+
Interfaces"](https://people.cs.vt.edu/~asandu/Courses/MTU/CS2911/fortran_notes/node181.html),
168+
Adrian Sandu. [last checked February 2026]
169+
170+
- ["Explicit
171+
Interfaces"](https://people.cs.vt.edu/~asandu/Courses/MTU/CS2911/fortran_notes/node182.html),
172+
Adrian Sandu. [last checked February 2026]
173+
174+
- ["Doctor Fortran Gets
175+
Explicit!"](https://web.archive.org/web/20130803094211/http://software.intel.com/en-us/forums/topic/275071),
176+
Steve Lionel. [last checked February 2026]
177+
178+
- ["Doctor Fortran Gets Explicit - Again!
179+
"](https://web.archive.org/web/20130113070703/http://software.intel.com/en-us/blogs/2012/01/05/doctor-fortran-gets-explicit-again),
180+
Steve Lionel. [last checked February 2026]

Checks/PWR089/benchmark/README.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Supplying excess arguments is undefined behavior, even if the affected code
2+
happens to work correctly. Thus, performance benchmarking isn't meaningful
3+
since there is no technically correct baseline to measure against.

Checks/PWR089/example.f90

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
! PWR089: Remove unexpected arguments from procedure calls
2+
3+
! NOT-PWR068: External procedure used to demonstrate how compilers can't catch
4+
! the argument count mismatch
5+
program call_with_excess_arguments
6+
use iso_fortran_env, only: real32
7+
implicit none
8+
9+
external :: logField
10+
character(len=20) :: name
11+
integer :: step
12+
real(kind=real32) :: val
13+
logical :: verbose
14+
15+
name = "field"
16+
step = 10
17+
val = 3.14
18+
verbose = .true.
19+
20+
call logField(name, step, val, verbose)
21+
end program call_with_excess_arguments

Checks/PWR089/io.f90

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
! PWR089: Remove unexpected arguments from procedure calls
2+
3+
subroutine logField(name, step, value)
4+
use iso_fortran_env, only: real32
5+
implicit none
6+
7+
character(*), intent(in) :: name
8+
integer, intent(in) :: step
9+
real(kind=real32), intent(in) :: value
10+
11+
write(*, *) trim(name), " step=", step, " value=", value
12+
end subroutine logField

Checks/PWR089/solution.f90

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
! PWR089: Remove unexpected arguments from procedure calls
2+
3+
! NOT-PWR068: External procedure used to demonstrate how compilers can't catch
4+
! the argument count mismatch
5+
program correct_call
6+
use iso_fortran_env, only: real32
7+
implicit none
8+
9+
external :: logField
10+
character(len=20) :: name
11+
integer :: step
12+
real(kind=real32) :: val
13+
14+
name = "field"
15+
step = 10
16+
val = 3.14
17+
18+
call logField(name, step, val)
19+
end program correct_call

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ designed to demonstrate:
9696
| [PWR080](Checks/PWR080/) | Conditionally initialized variables can lead to undefined behavior | correctness, portability, security | [CWE-758](https://cwe.mitre.org/data/definitions/758.html), [CWE-908](https://cwe.mitre.org/data/definitions/908.html), [CWE-909](https://cwe.mitre.org/data/definitions/909.html) | [6.13](https://j3-fortran.org/doc/year/23/23-241.pdf), [6.22](https://j3-fortran.org/doc/year/23/23-241.pdf), [6.56](https://j3-fortran.org/doc/year/23/23-241.pdf) | [EXP33-C](https://wiki.sei.cmu.edu/confluence/display/c/EXP33-C.+Do+not+read+uninitialized+memory), [EXP34-C](https://wiki.sei.cmu.edu/confluence/display/c/EXP34-C.+Do+not+dereference+null+pointers), [MSC15-C](https://wiki.sei.cmu.edu/confluence/display/c/MSC15-C.+Do+not+depend+on+undefined+behavior) | [EXP53-CPP](https://wiki.sei.cmu.edu/confluence/spaces/cplusplus/pages/88046609/EXP53-CPP.+Do+not+read+uninitialized+memory) | ✓ | ✓ | ✓ | |
9797
| [PWR081](Checks/PWR081/) | Uninitialized output arguments can lead to undefined behavior | correctness, portability, security | [CWE-758](https://cwe.mitre.org/data/definitions/758.html), [CWE-908](https://cwe.mitre.org/data/definitions/908.html), [CWE-909](https://cwe.mitre.org/data/definitions/909.html) | [6.13](https://j3-fortran.org/doc/year/23/23-241.pdf), [6.22](https://j3-fortran.org/doc/year/23/23-241.pdf), [6.56](https://j3-fortran.org/doc/year/23/23-241.pdf) | [EXP33-C](https://wiki.sei.cmu.edu/confluence/display/c/EXP33-C.+Do+not+read+uninitialized+memory), [EXP34-C](https://wiki.sei.cmu.edu/confluence/display/c/EXP34-C.+Do+not+dereference+null+pointers), [MSC15-C](https://wiki.sei.cmu.edu/confluence/display/c/MSC15-C.+Do+not+depend+on+undefined+behavior) | | | ✓ | | |
9898
| [PWR083](Checks/PWR083/) | Match the types of dummy and actual arguments in procedure calls | correctness, security | [CWE-628](https://cwe.mitre.org/data/definitions/628.html) | [6.11](https://j3-fortran.org/doc/year/23/23-241.pdf), [6.32](https://j3-fortran.org/doc/year/23/23-241.pdf), [6.53](https://j3-fortran.org/doc/year/23/23-241.pdf) | [EXP37-C](https://wiki.sei.cmu.edu/confluence/display/c/EXP37-C.+Call+functions+with+the+correct+number+and+type+of+arguments) | | | ✓ | | |
99+
| [PWR089](Checks/PWR089/) | Remove unexpected arguments from procedure calls | correctness, security | [CWE-628](https://cwe.mitre.org/data/definitions/628.html) | [6.32](https://j3-fortran.org/doc/year/23/23-241.pdf) | [EXP37-C](https://wiki.sei.cmu.edu/confluence/display/c/EXP37-C.+Call+functions+with+the+correct+number+and+type+of+arguments) | | | ✓ | | |
99100
| [PWD002](Checks/PWD002/) | Unprotected multithreading reduction operation | correctness, security | [CWE-366](https://cwe.mitre.org/data/definitions/366.html), [CWE-820](https://cwe.mitre.org/data/definitions/820.html) | [6.61](https://j3-fortran.org/doc/year/23/23-241.pdf) | [CON07-C](https://wiki.sei.cmu.edu/confluence/display/c/CON07-C.+Ensure+that+compound+operations+on+shared+variables+are+atomic), [CON43-C](https://wiki.sei.cmu.edu/confluence/display/c/CON43-C.+Do+not+allow+data+races+in+multithreaded+code) | | ✓ | ✓ | ✓ | |
100101
| [PWD003](Checks/PWD003/) | Missing array range in data copy to the GPU | correctness, security | [CWE-131](https://cwe.mitre.org/data/definitions/131.html), [CWE-758](https://cwe.mitre.org/data/definitions/758.html) | [6.56](https://j3-fortran.org/doc/year/23/23-241.pdf) | [MSC15-C](https://wiki.sei.cmu.edu/confluence/display/c/MSC15-C.+Do+not+depend+on+undefined+behavior) | | ✓ | ✓ | ✓ | |
101102
| [PWD004](Checks/PWD004/) | Out-of-memory-bounds array access | correctness, security | [CWE-125](https://cwe.mitre.org/data/definitions/125.html), [CWE-787](https://cwe.mitre.org/data/definitions/787.html) | [6.8](https://j3-fortran.org/doc/year/23/23-241.pdf), [6.9](https://j3-fortran.org/doc/year/23/23-241.pdf), [6.10](https://j3-fortran.org/doc/year/23/23-241.pdf) | [ARR30-C](https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts) | | ✓ | ✓ | ✓ | |

0 commit comments

Comments
 (0)