[インデックス 12170] ファイルの概要
このコミットは、GoコンパイラのCフロントエンド(src/cmd/cc
)における、型(typ
)処理関数内の配列の範囲外アクセス(out-of-bounds array access)のバグを修正するものです。具体的には、ewidth
配列へのアクセスが、定義されている型定数(NTYPE
)の範囲を超えて行われる可能性があった問題を解決しています。
コミット
commit 436f297d1e8cb941d859a00467395a8c541035e6
Author: Anthony Martin <ality@pbrane.org>
Date: Thu Feb 23 14:28:16 2012 -0500
cc: fix an out of bounds array access
Alternatively, we could expand the ewidth array
in [568]c/txt.c to have NALLTYPES elements and
give all types above NTYPE a width of -1.
I don't think it's worth it since TDOT and TOLD
are the only two type values above NTYPE that
are passed to typ:
$ /tmp/cctypes
cc/dcl.c:683: t->down = typ(TOLD, T);\
cc/dcl.c:919: return typ(TDOT, T);\
$
Fixes #3063.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5694047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/436f297d1e8cb941d859a00467395a8c541035e6
元コミット内容
このコミットは、GoコンパイラのCフロントエンド(cc
)において、typ
関数がewidth
配列にアクセスする際に発生する可能性のある範囲外アクセスを修正します。コミットメッセージでは、ewidth
配列をNALLTYPES
要素に拡張し、NTYPE
を超えるすべての型に幅-1
を与える代替案も提示されていますが、TDOT
とTOLD
のみがNTYPE
を超える値としてtyp
関数に渡されるため、その必要はないと判断されています。この修正は、Issue #3063を解決します。
変更の背景
GoコンパイラのCフロントエンド(cc
)では、型の情報を管理するためにtyp
関数が使用され、その中でewidth
という配列が型の幅(サイズ)を格納するために利用されていました。しかし、特定の型定数(TDOT
やTOLD
など)がewidth
配列の有効なインデックス範囲(NTYPE
まで)を超えてtyp
関数に渡されると、配列の範囲外アクセスが発生し、プログラムのクラッシュや予期せぬ動作を引き起こす可能性がありました。
この問題は、Go Issue #3063として報告されており、コンパイラの安定性と堅牢性を確保するために修正が必要でした。コミットメッセージに記載されているように、TDOT
とTOLD
は、NTYPE
を超える値を持つ型定数でありながら、typ
関数に渡されることが確認されています。
前提知識の解説
- Goコンパイラ(
cc
): Go言語のソースコードを機械語に変換するコンパイラの一部です。特にsrc/cmd/cc
は、GoコンパイラのCフロントエンドとして機能し、C言語の構文解析や型チェックなど、低レベルな処理を担当します。Goコンパイラは、Go言語で書かれたコードを直接コンパイルするだけでなく、C言語のコードを扱うためのツールチェーンも含まれています。 - 型システム: プログラミング言語において、データの種類(整数、浮動小数点数、文字列など)を定義し、それらのデータがどのように操作できるかを規定する仕組みです。コンパイラは型システムを利用して、プログラムの正当性を検証し、メモリの割り当てや操作を最適化します。
typ
関数: GoコンパイラのCフロントエンドにおいて、新しい型オブジェクトを作成または既存の型情報を取得・操作するための関数であると推測されます。この関数は、引数として型を表す整数値(et
)と、関連する型情報(d
)を受け取ります。ewidth
配列:ewidth
は "element width" の略であると考えられ、Goコンパイラ内部で各型のメモリ上での幅(サイズ)をバイト単位で格納するために使用される配列です。例えば、int
型が4バイトであれば、ewidth[INT_TYPE_CONSTANT]
には4が格納されるといった具合です。NTYPE
: Goコンパイラ内部で定義されている、有効な型定数の最大値を示す定数です。ewidth
配列のサイズは、通常このNTYPE
に基づいて決定されます。TDOT
とTOLD
: これらはGoコンパイラ内部で使われる特殊な型定数です。コミットメッセージから、これらがNTYPE
よりも大きな値を持つことが示唆されており、通常のデータ型とは異なる特殊な意味を持つ型(例えば、可変引数リストの終端を示すTDOT
や、古い型定義を示すTOLD
など)である可能性があります。- 配列の範囲外アクセス (Out-of-bounds array access): プログラムが配列の境界を超えてメモリにアクセスしようとするときに発生するエラーです。これは、未定義の動作を引き起こし、プログラムのクラッシュ、データの破損、またはセキュリティ上の脆弱性につながる可能性があります。C言語のような低レベル言語では、プログラマがメモリ管理を直接行うため、このようなエラーが発生しやすいです。
技術的詳細
このコミットの核心は、src/cmd/cc/sub.c
ファイル内のtyp
関数におけるewidth
配列へのアクセス方法の変更です。
元のコードでは、typ
関数に渡された型定数et
が直接ewidth
配列のインデックスとして使用されていました。
t->width = ewidth[et];
しかし、et
の値がewidth
配列の有効なインデックス範囲(NTYPE
まで)を超えた場合、これは範囲外アクセスとなり、未定義の動作を引き起こします。コミットメッセージによると、TDOT
とTOLD
という型定数がNTYPE
よりも大きな値を持つにもかかわらず、typ
関数に渡されることが確認されていました。
修正後のコードでは、ewidth
配列にアクセスする前にet
の値がNTYPE
未満であるかをチェックする条件分岐が追加されました。
if(et < NTYPE)
t->width = ewidth[et];
else
t->width = -1; // for TDOT or TOLD in prototype
この変更により、et
がNTYPE
未満の場合のみewidth[et]
にアクセスし、それ以外の場合(つまり、TDOT
やTOLD
のようなNTYPE
を超える値の場合)はt->width
に-1
を設定するようになりました。-1
という値は、これらの特殊な型が具体的なメモリ幅を持たないことを示す、またはコンパイラがこれらの型を特別に処理する必要があることを示すマーカーとして機能すると考えられます。これにより、ewidth
配列の範囲外アクセスが防止され、コンパイラの安定性が向上しました。
コアとなるコードの変更箇所
変更はsrc/cmd/cc/sub.c
ファイルにあります。
--- a/src/cmd/cc/sub.c
+++ b/src/cmd/cc/sub.c
@@ -156,7 +156,10 @@ typ(int et, Type *d)
t->link = d;
t->down = T;
t->sym = S;
- t->width = ewidth[et];
+ if(et < NTYPE)
+ t->width = ewidth[et];
+ else
+ t->width = -1; // for TDOT or TOLD in prototype
t->offset = 0;
t->shift = 0;
t->nbits = 0;
コアとなるコードの解説
変更されたコードは、typ
関数内でType
構造体のwidth
フィールドを設定する部分です。
-
変更前:
t->width = ewidth[et];
この行は、
et
(要素型)をインデックスとしてewidth
配列から値を取得し、それをt->width
に直接代入していました。et
がewidth
配列の有効な範囲外の値(例えばNTYPE
以上)である場合、これは範囲外アクセスを引き起こします。 -
変更後:
if(et < NTYPE) t->width = ewidth[et]; else t->width = -1; // for TDOT or TOLD in prototype
この変更では、
if
文が追加され、et
がNTYPE
未満であるかどうかをチェックします。et < NTYPE
が真の場合:et
がewidth
配列の有効なインデックス範囲内にあるため、以前と同様にewidth[et]
の値がt->width
に代入されます。et < NTYPE
が偽の場合:et
がNTYPE
以上であることを意味します。この場合、ewidth
配列へのアクセスは行われず、代わりにt->width
に-1
が代入されます。コメントにあるように、これはTDOT
やTOLD
のような特殊な型がプロトタイプで渡された場合に適用されます。-1
は、これらの型が具体的なメモリ幅を持たないことを示すか、コンパイラがこれらの型を特別に処理する必要があることを示す値として機能します。
この修正により、ewidth
配列の範囲外アクセスが確実に防止され、コンパイラの堅牢性が向上しました。
関連リンク
- Go Issue #3063: https://github.com/golang/go/issues/3063
- Gerrit Change-Id:
I2222222222222222222222222222222222222222
(コミットメッセージのhttps://golang.org/cl/5694047
に対応するGerritの変更ID)
参考にした情報源リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Goコンパイラのソースコード(特に
src/cmd/cc
ディレクトリ) - Go Issue Tracker: https://github.com/golang/go/issues
- Gerrit Code Review: https://go-review.googlesource.com/
- C言語における配列の範囲外アクセスに関する一般的な情報源