[インデックス 19097] ファイルの概要
このコミットは、Goコンパイラの一部である cmd/6g
における、nacl
(Native Client) 環境でのメモリゼロ初期化に関するバグ修正です。具体的には、zerorange
関数が widthptr
の奇数倍のサイズを正しくゼロ初期化しない問題を修正しています。
コミット
commit bfbb2e827b3c86e0f85f7378667958d91726c928
Author: Keith Randall <khr@golang.org>
Date: Thu Apr 10 07:59:46 2014 -0700
cmd/6g: nacl: zero odd multiple of widthptr correctly
LGTM=iant
R=remyoudompheng, iant
CC=golang-codereviews
https://golang.org/cl/86270043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/bfbb2e827b3c86e0f85f7378667958d91726c928
元コミット内容
cmd/6g: nacl: zero odd multiple of widthptr correctly
変更の背景
このコミットは、GoコンパイラがGoogle Native Client (NaCl) 環境向けにコードを生成する際に発生する可能性のある、メモリ初期化の誤りを修正することを目的としています。
Goのランタイムやコンパイラは、新しいメモリ領域を確保した際に、セキュリティや予測可能性のためにその領域をゼロで初期化することがよくあります。これは、以前のデータが残っていることによる情報漏洩や、未初期化メモリへのアクセスによる未定義動作を防ぐためです。
zerorange
関数は、指定されたメモリ範囲をゼロで埋める役割を担っています。通常、この操作は効率化のためにレジスタ幅 (widthreg
) やポインタ幅 (widthptr
) の倍数で行われます。しかし、特定の条件下、特にNaClのような特殊な環境では、メモリのアライメントやアクセスパターンに制約がある場合があります。
このコミットの背景には、zerorange
関数が widthptr
の奇数倍のサイズを処理する際に、残りの部分を正しくゼロ初期化できていなかったという問題があったと考えられます。これは、特にNaCl環境でのコード生成において、特定のサイズのメモリ領域が要求された場合に、部分的に未初期化のままになる可能性を示唆しています。
前提知識の解説
- Goコンパイラ (
cmd/6g
): Go言語のコンパイラは、ターゲットアーキテクチャごとに異なるフロントエンドを持っています。cmd/6g
は、AMD64 (x86-64) アーキテクチャ向けのGoコンパイラのバックエンドを指します。Go 1.5以降、コンパイラはGo自身で書かれるようになりましたが、このコミットが作成された2014年時点では、まだC言語で書かれた部分が多く残っていました。 - Google Native Client (NaCl): Google Native Clientは、ウェブブラウザ内でネイティブコード(C/C++など)を安全に実行するためのサンドボックス技術です。NaClは、特定のセキュリティ制約(例えば、メモリのアライメントやアクセスパターンに関する厳格なルール)を課すことで、悪意のあるコードがシステムに損害を与えるのを防ぎます。Go言語もNaCl環境をサポートしており、GoプログラムをNaClモジュールとしてコンパイルすることが可能でした。
zerorange
関数: Goのランタイムやコンパイラが使用する内部関数で、指定されたメモリ範囲をゼロで埋める役割を担います。これは、ガベージコレクションによって再利用されるメモリや、新しく確保されたスタックフレームなどを初期化する際に重要です。widthptr
: ポインタの幅(サイズ)をバイト単位で表す定数です。64ビットシステムでは通常8バイト(64ビット)です。widthreg
: レジスタの幅(サイズ)をバイト単位で表す定数です。64ビットシステムでは通常8バイト(64ビット)です。zerorange
関数は、効率的なゼロ初期化のために、通常はレジスタ単位でメモリを操作します。D_SP+D_INDIR
: アセンブリコードにおけるアドレス指定モードの一つで、スタックポインタ (SP
) を基準とした間接アドレス指定を示します。frame+lo
は、スタックフレーム内のオフセットを示し、特定のメモリ位置を指します。AMOVQ
,AMOVL
: アセンブリ命令のニーモニックです。AMOVQ
: 64ビット(Quadword)のデータを移動する命令。AMOVL
: 32ビット(Longword)のデータを移動する命令。 これらは、レジスタからメモリへ、またはメモリからレジスタへデータを転送するために使用されます。
技術的詳細
zerorange
関数は、cnt
バイトのメモリ領域をゼロで初期化します。この関数は、効率のために一度に widthreg
バイト(通常は8バイト)ずつゼロを書き込むループを使用しています。
問題は、cnt
が widthreg
の倍数ではない場合、特に widthptr
の奇数倍である場合に発生していました。NaCl環境では、メモリのアライメントが厳しく、特定のサイズの書き込みが許可されない場合があります。
元のコードでは、cnt
が widthreg
の倍数でない場合、残りの部分の処理が不十分であった可能性があります。このコミットでは、cnt
が widthreg
の倍数ではないが、widthptr
の倍数である場合に、特別な処理を追加しています。
具体的には、cnt % widthreg != 0
の条件が真であり、かつ cnt % widthptr != 0
の場合に fatal
エラーを発生させることで、予期せぬ状況を検出しています。これは、zerorange
が widthptr
の倍数でなければならないという前提があることを示唆しています。
そして、cnt
が widthreg
の倍数ではないが widthptr
の倍数である場合(つまり、widthreg
が widthptr
の倍数でないか、または widthptr
が widthreg
の約数であるような特殊なケース)、残りの widthptr
分の領域を AMOVL
命令(32ビット移動)でゼロ初期化しています。これは、AMOVQ
(64ビット移動) が使えない状況で、より小さい単位でゼロを書き込む必要があることを示唆しています。
この修正により、zerorange
関数は、widthptr
の奇数倍のサイズであっても、残りの部分を正しくゼロ初期化できるようになり、NaCl環境でのメモリ安全性と正確性が向上します。
コアとなるコードの変更箇所
src/cmd/6g/ggen.c
ファイルの zerorange
関数に以下のコードが追加されました。
--- a/src/cmd/6g/ggen.c
+++ b/src/cmd/6g/ggen.c
@@ -73,6 +73,14 @@ zerorange(Prog *p, vlong frame, vlong lo, vlong hi, uint32 *ax)
p = appendpp(p, AMOVQ, D_CONST, 0, D_AX, 0);
*ax = 1;
}
+ if(cnt % widthreg != 0) {
+ // should only happen with nacl
+ if(cnt % widthptr != 0)
+ fatal("zerorange count not a multiple of widthptr %d", cnt);
+ p = appendpp(p, AMOVL, D_AX, 0, D_SP+D_INDIR, frame+lo);
+ lo += widthptr;
+ cnt -= widthptr;
+ }
if(cnt <= 4*widthreg) {
for(i = 0; i < cnt; i += widthreg) {
p = appendpp(p, AMOVQ, D_AX, 0, D_SP+D_INDIR, frame+lo+i);
コアとなるコードの解説
追加されたコードブロックは、zerorange
関数の冒頭近くに位置し、cnt
(ゼロ初期化するバイト数) が widthreg
(レジスタ幅、通常8バイト) の倍数でない場合の特殊な処理を扱っています。
if(cnt % widthreg != 0)
:- この条件は、ゼロ初期化すべきバイト数
cnt
が、通常の一括処理単位であるwidthreg
の倍数ではない場合に真となります。これは、残りのバイトが存在することを示します。
- この条件は、ゼロ初期化すべきバイト数
// should only happen with nacl
:- コメントは、この特殊なケースが主にNaCl環境で発生することを指摘しています。これは、NaClが特定のメモリサイズやアライメントに制約を課すため、通常の環境では発生しないような、
widthreg
の倍数ではないがwidthptr
の倍数であるようなサイズが要求される可能性があることを示唆しています。
- コメントは、この特殊なケースが主にNaCl環境で発生することを指摘しています。これは、NaClが特定のメモリサイズやアライメントに制約を課すため、通常の環境では発生しないような、
if(cnt % widthptr != 0)
:- この内部の
if
文は、cnt
がwidthreg
の倍数ではないにもかかわらず、さらにwidthptr
(ポインタ幅、通常8バイト) の倍数でもない場合にfatal
エラーを発生させます。これは、zerorange
関数が処理するメモリ範囲のサイズは、少なくともwidthptr
の倍数であるべきだという強い前提があることを示しています。もしこの条件が満たされない場合、それは予期せぬ、おそらく不正なメモリ操作を意味するため、プログラムを終了させます。
- この内部の
p = appendpp(p, AMOVL, D_AX, 0, D_SP+D_INDIR, frame+lo);
:- この行が修正の核心です。
cnt
がwidthreg
の倍数ではないがwidthptr
の倍数である場合、残りのwidthptr
バイトをゼロ初期化します。 AMOVL
命令は32ビット(4バイト)のデータを移動します。D_AX
レジスタにはゼロが設定されているため、これはframe+lo
のアドレスに4バイトのゼロを書き込むことを意味します。- なぜ
AMOVL
なのか?widthptr
が8バイトであるにもかかわらず、AMOVL
(32ビット) が使われているのは、NaClのセキュリティモデルやアライメント制約により、特定の状況下で64ビットのAMOVQ
が直接使えない、あるいは部分的な書き込みが必要な場合に、より小さい単位で書き込む必要があるためと考えられます。これにより、残りのwidthptr
バイトのうち、最初の4バイトがゼロになります。 - 補足: このコードスニペットだけでは、残りの4バイトがどのようにゼロになるのかが明確ではありません。しかし、Goのコンパイラは通常、このような低レベルの最適化やアライメント処理を非常に慎重に行います。この
AMOVL
がwidthptr
の残りの部分をカバーするための最初のステップであるか、あるいは特定のNaClの制約下でwidthptr
が4バイトとして扱われるケースがあるか、または後続のループが残りを処理する前提があるか、といった可能性が考えられます。元のコミットの完全なコンテキストや、NaClの当時の仕様を詳細に確認する必要がありますが、この変更は少なくともwidthptr
の先頭部分を確実にゼロにすることを意図しています。
- この行が修正の核心です。
lo += widthptr;
:- ゼロ初期化された
widthptr
分の領域をスキップするために、開始オフセットlo
をwidthptr
だけ進めます。
- ゼロ初期化された
cnt -= widthptr;
:- ゼロ初期化された
widthptr
分のバイト数をcnt
から減算します。これにより、残りのcnt
バイトは後続のメインループで処理されることになります。
- ゼロ初期化された
この修正により、zerorange
関数は、widthreg
の倍数ではないが widthptr
の倍数であるような、特にNaCl環境で発生しうる「奇妙な」サイズのメモリ領域に対しても、正しくゼロ初期化を実行できるようになります。
関連リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Goのコードレビューシステム (Gerrit): https://go-review.googlesource.com/
- Google Native Client (NaCl) の概要 (古い情報): https://developer.chrome.com/docs/native-client/ (現在はWebAssemblyに置き換えられています)
参考にした情報源リンク
- Goのコミットメッセージと差分: https://github.com/golang/go/commit/bfbb2e827b3c86e0f85f7378667958d91726c928
- Goのコードレビュー (CL 86270043): https://golang.org/cl/86270043 (このリンクは現在、Gerritの新しいURLにリダイレクトされるはずです)
- Goコンパイラの歴史と進化に関する情報 (一般的なGoのドキュメントやブログ記事)
- Google Native Clientに関する一般的な情報 (当時の技術資料)
- アセンブリ言語の基本(
MOV
命令など)に関する一般的な情報```markdown
[インデックス 19097] ファイルの概要
このコミットは、Goコンパイラの一部である cmd/6g
における、nacl
(Native Client) 環境でのメモリゼロ初期化に関するバグ修正です。具体的には、zerorange
関数が widthptr
の奇数倍のサイズを正しくゼロ初期化しない問題を修正しています。
コミット
commit bfbb2e827b3c86e0f85f7378667958d91726c928
Author: Keith Randall <khr@golang.org>
Date: Thu Apr 10 07:59:46 2014 -0700
cmd/6g: nacl: zero odd multiple of widthptr correctly
LGTM=iant
R=remyoudompheng, iant
CC=golang-codereviews
https://golang.org/cl/86270043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/bfbb2e827b3c86e0f85f7378667958d91726c928
元コミット内容
cmd/6g: nacl: zero odd multiple of widthptr correctly
変更の背景
このコミットは、GoコンパイラがGoogle Native Client (NaCl) 環境向けにコードを生成する際に発生する可能性のある、メモリ初期化の誤りを修正することを目的としています。
Goのランタイムやコンパイラは、新しいメモリ領域を確保した際に、セキュリティや予測可能性のためにその領域をゼロで初期化することがよくあります。これは、以前のデータが残っていることによる情報漏洩や、未初期化メモリへのアクセスによる未定義動作を防ぐためです。
zerorange
関数は、指定されたメモリ範囲をゼロで埋める役割を担っています。通常、この操作は効率化のためにレジスタ幅 (widthreg
) やポインタ幅 (widthptr
) の倍数で行われます。しかし、特定の条件下、特にNaClのような特殊な環境では、メモリのアライメントやアクセスパターンに制約がある場合があります。
このコミットの背景には、zerorange
関数が widthptr
の奇数倍のサイズを処理する際に、残りの部分を正しくゼロ初期化できていなかったという問題があったと考えられます。これは、特にNaCl環境でのコード生成において、特定のサイズのメモリ領域が要求された場合に、部分的に未初期化のままになる可能性を示唆しています。
前提知識の解説
- Goコンパイラ (
cmd/6g
): Go言語のコンパイラは、ターゲットアーキテクチャごとに異なるフロントエンドを持っています。cmd/6g
は、AMD64 (x86-64) アーキテクチャ向けのGoコンパイラのバックエンドを指します。Go 1.5以降、コンパイラはGo自身で書かれるようになりましたが、このコミットが作成された2014年時点では、まだC言語で書かれた部分が多く残っていました。 - Google Native Client (NaCl): Google Native Clientは、ウェブブラウザ内でネイティブコード(C/C++など)を安全に実行するためのサンドボックス技術です。NaClは、特定のセキュリティ制約(例えば、メモリのアライメントやアクセスパターンに関する厳格なルール)を課すことで、悪意のあるコードがシステムに損害を与えるのを防ぎます。Go言語もNaCl環境をサポートしており、GoプログラムをNaClモジュールとしてコンパイルすることが可能でした。
zerorange
関数: Goのランタイムやコンパイラが使用する内部関数で、指定されたメモリ範囲をゼロで埋める役割を担います。これは、ガベージコレクションによって再利用されるメモリや、新しく確保されたスタックフレームなどを初期化する際に重要です。widthptr
: ポインタの幅(サイズ)をバイト単位で表す定数です。64ビットシステムでは通常8バイト(64ビット)です。widthreg
: レジスタの幅(サイズ)をバイト単位で表す定数です。64ビットシステムでは通常8バイト(64ビット)です。zerorange
関数は、効率的なゼロ初期化のために、通常はレジスタ単位でメモリを操作します。D_SP+D_INDIR
: アセンブリコードにおけるアドレス指定モードの一つで、スタックポインタ (SP
) を基準とした間接アドレス指定を示します。frame+lo
は、スタックフレーム内のオフセットを示し、特定のメモリ位置を指します。AMOVQ
,AMOVL
: アセンブリ命令のニーモニックです。AMOVQ
: 64ビット(Quadword)のデータを移動する命令。AMOVL
: 32ビット(Longword)のデータを移動する命令。 これらは、レジスタからメモリへ、またはメモリからレジスタへデータを転送するために使用されます。
技術的詳細
zerorange
関数は、cnt
バイトのメモリ領域をゼロで初期化します。この関数は、効率のために一度に widthreg
バイト(通常は8バイト)ずつゼロを書き込むループを使用しています。
問題は、cnt
が widthreg
の倍数ではない場合、特に widthptr
の奇数倍である場合に発生していました。NaCl環境では、メモリのアライメントが厳しく、特定のサイズの書き込みが許可されない場合があります。
元のコードでは、cnt
が widthreg
の倍数でない場合、残りの部分の処理が不十分であった可能性があります。このコミットでは、cnt
が widthreg
の倍数ではないが、widthptr
の倍数である場合に、特別な処理を追加しています。
具体的には、cnt % widthreg != 0
の条件が真であり、かつ cnt % widthptr != 0
の場合に fatal
エラーを発生させることで、予期せぬ状況を検出しています。これは、zerorange
が widthptr
の倍数でなければならないという前提があることを示唆しています。
そして、cnt
が widthreg
の倍数ではないが widthptr
の倍数である場合(つまり、widthreg
が widthptr
の倍数でないか、または widthptr
が widthreg
の約数であるような特殊なケース)、残りの widthptr
分の領域を AMOVL
命令(32ビット移動)でゼロ初期化しています。これは、AMOVQ
(64ビット移動) が使えない状況で、より小さい単位でゼロを書き込む必要があることを示唆しています。
この修正により、zerorange
関数は、widthptr
の奇数倍のサイズであっても、残りの部分を正しくゼロ初期化できるようになり、NaCl環境でのメモリ安全性と正確性が向上します。
コアとなるコードの変更箇所
src/cmd/6g/ggen.c
ファイルの zerorange
関数に以下のコードが追加されました。
--- a/src/cmd/6g/ggen.c
+++ b/src/cmd/6g/ggen.c
@@ -73,6 +73,14 @@ zerorange(Prog *p, vlong frame, vlong lo, vlong hi, uint32 *ax)
p = appendpp(p, AMOVQ, D_CONST, 0, D_AX, 0);
*ax = 1;
}
+ if(cnt % widthreg != 0) {
+ // should only happen with nacl
+ if(cnt % widthptr != 0)
+ fatal("zerorange count not a multiple of widthptr %d", cnt);
+ p = appendpp(p, AMOVL, D_AX, 0, D_SP+D_INDIR, frame+lo);
+ lo += widthptr;
+ cnt -= widthptr;
+ }
if(cnt <= 4*widthreg) {
for(i = 0; i < cnt; i += widthreg) {
p = appendpp(p, AMOVQ, D_AX, 0, D_SP+D_INDIR, frame+lo+i);
コアとなるコードの解説
追加されたコードブロックは、zerorange
関数の冒頭近くに位置し、cnt
(ゼロ初期化するバイト数) が widthreg
(レジスタ幅、通常8バイト) の倍数でない場合の特殊な処理を扱っています。
if(cnt % widthreg != 0)
:- この条件は、ゼロ初期化すべきバイト数
cnt
が、通常の一括処理単位であるwidthreg
の倍数ではない場合に真となります。これは、残りのバイトが存在することを示します。
- この条件は、ゼロ初期化すべきバイト数
// should only happen with nacl
:- コメントは、この特殊なケースが主にNaCl環境で発生することを指摘しています。これは、NaClが特定のメモリサイズやアライメントに制約を課すため、通常の環境では発生しないような、
widthreg
の倍数ではないがwidthptr
の倍数であるようなサイズが要求される可能性があることを示唆しています。
- コメントは、この特殊なケースが主にNaCl環境で発生することを指摘しています。これは、NaClが特定のメモリサイズやアライメントに制約を課すため、通常の環境では発生しないような、
if(cnt % widthptr != 0)
:- この内部の
if
文は、cnt
がwidthreg
の倍数ではないにもかかわらず、さらにwidthptr
(ポインタ幅、通常8バイト) の倍数でもない場合にfatal
エラーを発生させます。これは、zerorange
関数が処理するメモリ範囲のサイズは、少なくともwidthptr
の倍数であるべきだという強い前提があることを示しています。もしこの条件が満たされない場合、それは予期せぬ、おそらく不正なメモリ操作を意味するため、プログラムを終了させます。
- この内部の
p = appendpp(p, AMOVL, D_AX, 0, D_SP+D_INDIR, frame+lo);
:- この行が修正の核心です。
cnt
がwidthreg
の倍数ではないがwidthptr
の倍数である場合、残りのwidthptr
バイトをゼロ初期化します。 AMOVL
命令は32ビット(4バイト)のデータを移動します。D_AX
レジスタにはゼロが設定されているため、これはframe+lo
のアドレスに4バイトのゼロを書き込むことを意味します。- なぜ
AMOVL
なのか?widthptr
が8バイトであるにもかかわらず、AMOVL
(32ビット) が使われているのは、NaClのセキュリティモデルやアライメント制約により、特定の状況下で64ビットのAMOVQ
が直接使えない、あるいは部分的な書き込みが必要な場合に、より小さい単位で書き込む必要があるためと考えられます。これにより、残りのwidthptr
バイトのうち、最初の4バイトがゼロになります。 - 補足: このコードスニペットだけでは、残りの4バイトがどのようにゼロになるのかが明確ではありません。しかし、Goのコンパイラは通常、このような低レベルの最適化やアライメント処理を非常に慎重に行います。この
AMOVL
がwidthptr
の残りの部分をカバーするための最初のステップであるか、あるいは特定のNaClの制約下でwidthptr
が4バイトとして扱われるケースがあるか、または後続のループが残りを処理する前提があるか、といった可能性が考えられます。元のコミットの完全なコンテキストや、NaClの当時の仕様を詳細に確認する必要がありますが、この変更は少なくともwidthptr
の先頭部分を確実にゼロにすることを意図しています。
- この行が修正の核心です。
lo += widthptr;
:- ゼロ初期化された
widthptr
分の領域をスキップするために、開始オフセットlo
をwidthptr
だけ進めます。
- ゼロ初期化された
cnt -= widthptr;
:- ゼロ初期化された
widthptr
分のバイト数をcnt
から減算します。これにより、残りのcnt
バイトは後続のメインループで処理されることになります。
- ゼロ初期化された
この修正により、zerorange
関数は、widthreg
の倍数ではないが widthptr
の倍数であるような、特にNaCl環境で発生しうる「奇妙な」サイズのメモリ領域に対しても、正しくゼロ初期化を実行できるようになります。
関連リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Goのコードレビューシステム (Gerrit): https://go-review.googlesource.com/
- Google Native Client (NaCl) の概要 (古い情報): https://developer.chrome.com/docs/native-client/ (現在はWebAssemblyに置き換えられています)
参考にした情報源リンク
- Goのコミットメッセージと差分: https://github.com/golang/go/commit/bfbb2e827b3c86e0f85f7378667958d91726c928
- Goのコードレビュー (CL 86270043): https://golang.org/cl/86270043 (このリンクは現在、Gerritの新しいURLにリダイレクトされるはずです)
- Goコンパイラの歴史と進化に関する情報 (一般的なGoのドキュメントやブログ記事)
- Google Native Clientに関する一般的な情報 (当時の技術資料)
- アセンブリ言語の基本(
MOV
命令など)に関する一般的な情報