配列の範囲外アクセス

永井 忠一 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

どちらも、警告扱い

FortranPascal
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言語

言語仕様で、未定義(undefined)と定めている

コンパイラやコンパイルオプションによっては、コンパイルエラーにできる場合がある

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

確認した教科書


配列の要素数

(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
FortranC
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なコードでは、未初期化の配列を定義できない

FortranC
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では、未初期化の配列はゼロクリアされる

GoD
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