配列の範囲外アクセス
永井 忠一 2025.10.5
Fortran と Pascal
言語仕様では明確にしていない
GFortran の man
$ man gfortran
-fcheck=<keyword>
Enable the generation of run-time checks; the argument shall be a comma-delimited list of the following keywords. Prefixing a check with no- disables it if it was activated by a previous specification.
all Enable all run-time test of -fcheck.
...snip...
bounds
Enable generation of run-time checks for array subscripts and against the declared minimum and maximum values. It also checks array indices for assumed and deferred shape arrays against the actual allocated bounds and ensures that all string lengths are equal for character array constructors without an explicit typespec.
Some checks require that -fcheck=bounds is set for the compilation of the main program.
Note: In the future this may also include other forms of checking, e.g., checking substring references.
...snip...
コンパイラによっては、コンパイルオプションが利用できる。実行時のチェック
| Fortran |
PROGRAM main
IMPLICIT NONE
INTEGER, DIMENSION(0:2) :: a = (/ 1, 2, 3 /)
INTEGER i
WRITE(*,*) 'hello, Fortran'
READ(*,*) i
WRITE(*,*) a(i) ! bounds check
END PROGRAM main
$ gfortran --version
GNU Fortran (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
Copyright (C) 2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ gfortran -fcheck=bounds ooba_dyn.f90 && ./a.out
hello, Fortran
3
At line 7 of file ooba_dyn.f90
Fortran runtime error: Index '3' of dimension 1 of array 'a' above upper bound of 2
Error termination. Backtrace:
#0 0x752295a23e59 in ???
#1 0x752295a24a71 in ???
#2 0x752295a25082 in ???
#3 0x5bfb04cbd337 in ???
#4 0x5bfb04cbd3ac in ???
#5 0x75229562a1c9 in __libc_start_call_main
at ../sysdeps/nptl/libc_start_call_main.h:58
#6 0x75229562a28a in __libc_start_main_impl
at ../csu/libc-start.c:360
#7 0x5bfb04cbd104 in ???
#8 0xffffffffffffffff in ???
|
Pascalでは、コンパイラディレクティブ「{$R+}」が使える場合がある。コンパイルエラーになる例
| Pascal |
{$R+}
program main;
var a: array [0..2] of integer;
begin
a[0] := 1; a[1] := 2; a[2] := 3;
writeln('hello, Pascal');
writeln(a[3]); { out-of-bounds access }
end.
$ fpc ooba.pas
Free Pascal Compiler version 3.2.2+dfsg-32 [2024/01/05] for x86_64
Copyright (c) 1993-2021 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling ooba.pas
ooba.pas(7,13) Error: range check error while evaluating constants (3 must be between 0 and 2)
ooba.pas(9) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted
Error: /usr/bin/ppcx64 returned an error exitcode
|
実行時エラーの例
| Pascal |
{$R+}
program main;
var
a: array [0..2] of integer;
i: integer;
begin
a[0] := 1; a[1] := 2; a[2] := 3;
writeln('hello, Pascal');
readln(i);
writeln(a[i]); { bounds check }
end.
$ fpc ooba_dyn.pas -oa.out
Free Pascal Compiler version 3.2.2+dfsg-32 [2024/01/05] for x86_64
Copyright (c) 1993-2021 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling ooba_dyn.pas
Linking a.out
11 lines compiled, 0.0 sec
$ ./a.out
hello, Pascal
3
Runtime error 201 at $0000000000401156
$0000000000401156
$0000000000422F6C
|
どちらも、警告扱い
| Fortran | Pascal |
PROGRAM main
IMPLICIT NONE
INTEGER, DIMENSION(0:2) :: a = (/ 1, 2, 3 /)
WRITE(*,*) 'hello, Fortran'
WRITE(*,*) a(3) ! out-of-bounds access
END PROGRAM main
$ gfortran ooba.f90
ooba.f90:5:15:
5 | WRITE(*,*) a(3)
| 1
Warning: Array reference at (1) is out of bounds (3 > 2) in dimension 1
$ echo $?
0
|
program main;
var a: array [0..2] of integer;
begin
a[0] := 1; a[1] := 2; a[2] := 3;
writeln('hello, Pascal');
writeln(a[3]); { out-of-bounds access }
end.
$ fpc ooba_static.pas
Free Pascal Compiler version 3.2.2+dfsg-32 [2024/01/05] for x86_64
Copyright (c) 1993-2021 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling ooba_static.pas
ooba_static.pas(6,14) Warning: range check error while evaluating constants (3 must be between 0 and 2)
Linking ooba_static
7 lines compiled, 0.0 sec
1 warning(s) issued
$ echo $?
0
|
補足
Free Pascal Compilerのインストール。私の環境では Linux apt
$ sudo apt install fp-compiler-3.2.2
C言語
言語仕様で、未定義と定めている
コンパイラやコンパイルオプションによっては、コンパイルエラーにできる場合がある
| C |
#include <stdio.h>
int main(int argc, char *argv[]) {
int a[] = {1, 2, 3};
printf("hello, C\n");
printf("%d\n", a[3]); /* out-of-bounds access */
}
$ gcc -O2 -Warray-bounds -Werror ooba.c
ooba.c: In function ‘main’:
ooba.c:6:9: error: array subscript 3 is above array bounds of ‘int[3]’ [-Werror=array-bounds=]
6 | printf("%d\n", a[3]); /* out-of-bounds access */
| ^~~~~~~~~~~~~~~~~~~~
ooba.c:5:13: note: while referencing ‘a’
5 | int a[] = {1, 2, 3};
| ^
cc1: all warnings being treated as errors
|
サニタイザを利用する方法
| C |
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int a[] = {1, 2, 3};
int i = argc >= 2 ? atoi(argv[1]) : 0;
printf("hello, C\n");
printf("%d\n", a[i]); /* bounds check */
}
$ g++ -Wall -g -fsanitize=address ooba_dyn.c && ./a.out 3
hello, C
=================================================================
==7770==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x76d09a30002c at pc 0x62ef3ef8f499 bp 0x7ffc17d2e360 sp 0x7ffc17d2e350
READ of size 4 at 0x76d09a30002c thread T0
#0 0x62ef3ef8f498 in main /home/nagai/...snip.../ooba_dyn.c:8
#1 0x76d09c42a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#2 0x76d09c42a28a in __libc_start_main_impl ../csu/libc-start.c:360
#3 0x62ef3ef8f1e4 in _start (/home/nagai/...snip.../a.out+0x11e4) (BuildId: 7c0d3e5ccb091b3785802324a5ca0c2e006b1869)
Address 0x76d09a30002c is located in stack of thread T0 at offset 44 in frame
#0 0x62ef3ef8f2b8 in main /home/nagai/...snip.../ooba_dyn.c:4
This frame has 1 object(s):
[32, 44) 'a' (line 5) <== Memory access at offset 44 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /home/nagai/...snip.../ooba_dyn.c:8 in main
Shadow bytes around the buggy address:
0x76d09a2ffd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x76d09a2ffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x76d09a2ffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x76d09a2fff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x76d09a2fff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x76d09a300000: f1 f1 f1 f1 00[04]f3 f3 00 00 00 00 00 00 00 00
0x76d09a300080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x76d09a300100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x76d09a300180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x76d09a300200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x76d09a300280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==7770==ABORTING
|
Rust
未定義の振る舞いを避けるために、コンパイラはエラーを検出する
コンパイル時にチェック
| Rust |
fn main() {
println!("hello, Rust");
let a = [1, 2, 3];
println!("{}", a[3]); // out-of-bounds access
}
$ rustc ooba.rs
error: this operation will panic at runtime
--> ooba.rs:4:20
|
4 | println!("{}", a[3]); // out-of-bounds access
| ^^^^ index out of bounds: the length is 3 but the index is 3
|
= note: `#[deny(unconditional_panic)]` on by default
error: aborting due to previous error
|
実行時にチェック
| Rust |
fn main() {
println!("hello, Rust");
let a = [1, 2, 3];
let args: Vec<String> = std::env::args().collect();
if args.len() >= 2 {
let i: usize = args[1].parse().unwrap();
println!("{}", a[i]); // bounds check
}
}
$ rustc ooba_dyn.rs -o a.out && ./a.out 3
hello, Rust
thread 'main' panicked at ooba_dyn.rs:7:24:
index out of bounds: the len is 3 but the index is 3
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
Go
スライスを使用する場合には、実行時のチェックになる
| Go |
package main
import "fmt"
func main() {
fmt.Println("hello, Go")
s := []int{1, 2, 3} // slice type
fmt.Println(s[3]) // bounds check
}
$ go run ooba.go
hello, Go
panic: runtime error: index out of range [3] with length 3
goroutine 1 [running]:
main.main()
/home/nagai/...snip.../ooba.go:8 +0x59
exit status 2
|
配列を使用して、コンパイルエラーにできる場合
| Go |
package main
import "fmt"
func main() {
fmt.Println("hello, Go")
a := [3]int{1, 2, 3} // array type
fmt.Println(a[3]) // out-of-bounds access
}
$ go build ooba_arr.go
# command-line-arguments
./ooba_arr.go:8:16: invalid argument: index 3 out of bounds [0:3]
|
コンパイル時には決定できない場合
| Go |
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
fmt.Println("hello, Go")
a := [3]int{1, 2, 3} // array type
if len(os.Args) >= 2 {
i, err := strconv.Atoi(os.Args[1])
if err != nil { os.Exit(1) }
fmt.Println(a[i]) // bounds check
}
}
$ go run ooba_dyn.go 3
hello, Go
panic: runtime error: index out of range [3] with length 3
goroutine 1 [running]:
main.main()
/home/nagai/...snip.../ooba_dyn.go:15 +0x106
exit status 2
|
(Goで、len() は、組み込み関数)
D言語
例外として処理される
| D |
import std.stdio;
void main() {
writeln("hello, D");
auto a = [1, 2, 3];
writeln(a[3]); // bounds check
}
|
$ ldc2 -run ooba.d
hello, D
core.exception.ArrayIndexError@ooba.d(6): index [3] is out of bounds for array of length 3
----------------
??:? onArrayIndexError [0x71f0ac16e54d]
??:? _d_arraybounds_index [0x71f0ac16eb6d]
??:? [0x5859e5e1036e]
??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int function(char[][])*).runAll() [0x71f0ac1a5dcc]
??:? _d_run_main2 [0x71f0ac1a5be6]
??:? _d_run_main [0x71f0ac1a5a3c]
??:? [0x5859e5e104d1]
??:? [0x71f0abe2a1c9]
??:? __libc_start_main [0x71f0abe2a28a]
??:? [0x5859e5e10214]
Error: /tmp/ooba-49f5c0 failed with status: 1
$ gdc ooba.d && ./a.out
hello, D
core.exception.ArrayIndexError@ooba.d(6): index [3] is out of bounds for array of length 3
----------------
??:? _d_createTrace [0x786196f59d1b]
??:? _d_throw [0x786196f4ee54]
??:? onArrayIndexError [0x786196f2d694]
??:? _d_arraybounds_indexp [0x786196f2d6d2]
??:? _Dmain [0x5938e294b3ae]
|
例外「ArrayIndexError」をキャッチ
| D |
import std.stdio;
import core.exception;
void main() {
writeln("hello, D");
auto a = [1, 2, 3];
try {
writeln(a[3]); // bounds check
} catch (ArrayIndexError e) {
writeln(typeid(e).name);
writeln(e.msg);
}
}
|
$ ldc2 -run ooba_try.d
hello, D
core.exception.ArrayIndexError
index [3] is out of bounds for array of length 3
$ gdc ooba_try.d && ./a.out
hello, D
core.exception.ArrayIndexError
index [3] is out of bounds for array of length 3
|
(備忘録)
私の環境では Linux apt
$ sudo apt install golang-go elpa-go-mode
$ sudo apt install ldc gdc
確認した教科書
- 間野 浩太郎 監修, 内田 智史 著, 『TURBO Pascal プログラミング』, オーム社, 1990
- Peter van der Linden 著, 梅原 系 訳, 『エキスパートCプログラミング』, アスキー, 1996
- 柴田 望洋 著, 『新版 秘伝C言語問答 ポインタ編』, ソフトバンクパブリッシング, 1997
- The Rust Programming Language 日本語版
配列の要素数
(High()、Low() や)SizeOf() は、標準ではない
| Pascal |
program main;
var a: array [0..2] of integer;
begin
{ writeln(High(a) - Low(a) + 1); }
writeln(SizeOf(a) div SizeOf(a[0]));
end.
$ fpc arr_len.pas -oa.out
Free Pascal Compiler version 3.2.2+dfsg-32 [2024/01/05] for x86_64
Copyright (c) 1993-2021 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling arr_len.pas
Linking a.out
6 lines compiled, 0.0 sec
$ ./a.out
3
|
- (FORTRAN 77には、SIZE() は無い。Fortran 90から導入)
- (C言語では、ポインタからは、配列の長さを知ることはできない)
| Fortran | C |
PROGRAM main
IMPLICIT NONE
INTEGER, DIMENSION(0:2) :: a
WRITE(*,*) SIZE(a)
END PROGRAM main
$ gfortran -Wall arr_len.f90 && ./a.out
3
|
#include <stdio.h>
int main(int argc, char *argv[]) {
int a[3];
printf("%lu\n", sizeof(a)/sizeof(a[0]));
}
$ gcc -Wall arr_len.c && ./a.out
3
|
Rustは、safeなコードでは、未初期化の配列を定義できない
| Fortran | C |
PROGRAM main
IMPLICIT NONE
INTEGER, DIMENSION(0:2) :: a = 0
WRITE(*,*) SIZE(a)
END PROGRAM main
$ gfortran -Wall arr_len_init.f90 && ./a.out
3
|
#include <stdio.h>
int main(int argc, char *argv[]) {
int a[3] = { 0 };
printf("%lu\n", sizeof(a)/sizeof(a[0]));
}
$ gcc -Wall arr_len_init.c && ./a.out
3
|
| Rust |
fn main() {
let a = [0; 3];
println!("{}", a.len());
}
$ rustc arr_len.rs -o a.out && ./a.out
3
|
(Rustで、.len() は、配列のメソッド)
Goでは、未初期化変数(配列)はゼロクリアされる。Dでは、未初期化の配列はゼロクリアされる
| Go | D |
package main
import "fmt"
func main() {
var a [3]int
fmt.Println(len(a))
}
$ go run arr_len.go
3
|
import std.stdio;
void main() {
int[3] a;
writeln(a.length);
}
|
$ ldc2 -run arr_len.d
3
$ gdc arr_len.d && ./a.out
3
|
(Goで、len() は、組み込み関数)
(Dで、.length は、配列の組み込みプロパティ)
Kotlin
配列の範囲外アクセスは、例外「ArrayIndexOutOfBoundsException」として扱われる。(Kotlinで、.size は、読み取り専用のプロパティ)
| Kotlin |
package org.example
fun main() {
val a = arrayOf(1, 2, 3)
try {
println(a[3]) // bounds check
} catch (e: IndexOutOfBoundsException) {
println(e::class)
println(e.message)
}
}
> Task :org.example.MainKt.main()
class java.lang.ArrayIndexOutOfBoundsException (Kotlin reflection is not available)
Index 3 out of bounds for length 3
|
package org.example
fun main() {
val a = IntArray(3)
println(a.size)
}
> Task :org.example.MainKt.main()
3
|
(言語処理系は、OpenJDKと、IDE「IntelliJ IDEA Community Edition」を利用)
© 2025 Tadakazu Nagai