Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 18164] ファイルの概要

このコミットは、GoランタイムにおけるPlan 9オペレーティングシステム向けの乱数データ取得メカニズムの修正に関するものです。具体的には、Plan 9環境で/dev/randomデバイスが存在し、そこからエントロピー(乱数性)を取得できることを認識し、その機能を利用するように変更されました。

コミット

commit c136197ca8a536b6fd93f0ee2a7de6c6541c0124
Author: Jeff Sickel <jas@corpus-callosum.com>
Date:   Sat Jan 4 10:53:22 2014 -0800

    runtime: plan 9 does have /dev/random
    
    R=golang-codereviews, r, aram
    CC=0intro, golang-codereviews, rsc
    https://golang.org/cl/43420045

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/c136197ca8a536b6fd93f0ee2a7de6c6541c0124

元コミット内容

このコミットは、Goランタイムのos_plan9.cファイルにおいて、runtime·get_random_data関数が常にnil0を返していた(つまり、乱数データを取得しない)挙動を修正するものです。

変更前:

void
runtime·get_random_data(byte **rnd, int32 *rnd_len)
{
	*rnd = nil;
	*rnd_len = 0;
}

変更後:

void
runtime·get_random_data(byte **rnd, int32 *rnd_len)
{
	static byte random_data[HashRandomBytes];
	int32 fd;

	fd = runtime·open("/dev/random", 0 /* O_RDONLY */, 0);
	if(runtime·read(fd, random_data, HashRandomBytes) == HashRandomBytes) {
		*rnd = random_data;
		*rnd_len = HashRandomBytes;
	} else {
		*rnd = nil;
		*rnd_len = 0;
	}
	runtime·close(fd);
}

変更の背景

Goランタイムは、ハッシュテーブルのシード値など、様々な内部処理で乱数データ(エントロピー)を必要とします。これらの乱数データは、セキュリティ上の理由(例:ハッシュ衝突攻撃の緩和)や、予測不可能な動作の保証のために、高品質な乱数源から取得されるべきです。

以前のGoランタイムのPlan 9実装では、runtime·get_random_data関数が乱数データを取得する処理を実装しておらず、常に空のデータを返していました。これは、Plan 9オペレーティングシステムには/dev/randomのような乱数デバイスが存在しない、あるいは利用できないという誤解に基づいていた可能性があります。

このコミットの背景には、「Plan 9にも/dev/randomが存在し、利用可能である」という認識の更新があります。これにより、GoランタイムがPlan 9環境でも適切な乱数源からエントロピーを取得できるようになり、ハッシュテーブルのシード値などの生成において、より堅牢な乱数性を提供できるようになります。

前提知識の解説

Plan 9 from Bell Labs

Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Unixの設計思想をさらに推し進め、すべてのリソース(ファイル、デバイス、ネットワーク接続など)をファイルシステムとして表現するという「すべてはファイルである」という原則を徹底しています。これにより、システム内のあらゆる要素がファイル操作を通じてアクセス・制御できるようになっています。

/dev/random/dev/urandom

Unix系システム(およびPlan 9)において、/dev/random/dev/urandomは、カーネルが提供する擬似乱数生成器(PRNG)のインターフェースです。これらは、システムのエントロピー源(キーボード入力、マウスの動き、ディスクI/O、ネットワークイベントなど)から収集されたノイズを基に、高品質な乱数を生成します。

  • /dev/random: ブロッキング乱数源です。要求されたエントロピーが十分に蓄積されていない場合、エントロピーが利用可能になるまで読み出しをブロックします。これにより、常に高品質な乱数(真の乱数に近い)が保証されますが、エントロピーが枯渇するとシステムのパフォーマンスに影響を与える可能性があります。主に暗号鍵の生成など、高いセキュリティが要求される場面で使用されます。
  • /dev/urandom: 非ブロッキング乱数源です。エントロピーが不足している場合でも、利用可能なエントロピーから生成された乱数(擬似乱数)を返します。エントロピーが枯渇してもブロックしないため、パフォーマンスへの影響は少ないですが、エントロピーが枯渇した状態では予測可能性が高まる可能性があります。一般的な用途(セッションID、ソルトなど)で広く使用されます。

このコミットでは/dev/randomが使用されており、これはGoランタイムがハッシュシードなどの重要な用途に、より高品質な乱数を求めていることを示唆しています。

HashRandomBytes

HashRandomBytesは、Goランタイム内部で定義されている定数で、ハッシュテーブルのシード値として使用される乱数データのバイト数を指定します。この値は、ハッシュ衝突攻撃に対する耐性を高めるために、十分な長さの乱数シードを確保するために用いられます。具体的な値はGoのバージョンによって異なる場合がありますが、通常は数バイトから数十バイトの範囲です。このコミットでは、HashRandomBytes分のデータを/dev/randomから読み取ろうとしています。

技術的詳細

このコミットの技術的な核心は、GoランタイムがPlan 9環境で乱数データを取得する際の、より適切なシステムコールとファイル操作の利用にあります。

  1. 乱数データバッファの確保: static byte random_data[HashRandomBytes]; HashRandomBytesのサイズを持つ静的なバイト配列random_dataが宣言されます。staticキーワードにより、この配列は関数の呼び出し間でその内容を保持し、一度初期化されれば再利用されます。これは、乱数データを格納するための一時的なバッファとして機能します。

  2. /dev/randomのオープン: fd = runtime·open("/dev/random", 0 /* O_RDONLY */, 0); runtime·open関数は、Goランタイムが提供するシステムコールラッパーで、指定されたパスのファイルをオープンします。ここでは/dev/randomを読み取り専用モード(O_RDONLYは0に相当)でオープンしています。ファイルディスクリプタ(fd)が返されます。

  3. 乱数データの読み取り: if(runtime·read(fd, random_data, HashRandomBytes) == HashRandomBytes) { ... } runtime·read関数は、指定されたファイルディスクリプタからデータを読み取ります。ここでは、オープンした/dev/randomからHashRandomBytes分のデータをrandom_dataバッファに読み取ろうとしています。 読み取りが成功し、要求されたバイト数(HashRandomBytes)と実際に読み取れたバイト数が一致した場合、乱数データが正常に取得できたと判断されます。

  4. 乱数データのポインタと長さの設定: 読み取りが成功した場合: *rnd = random_data; *rnd_len = HashRandomBytes; 呼び出し元に、取得した乱数データが格納されているrandom_dataバッファへのポインタと、そのデータの長さ(HashRandomBytes)を渡します。

    読み取りが失敗した場合(例えば、/dev/randomが存在しない、読み取りエラーが発生した、または要求されたバイト数を読み取れなかった場合): *rnd = nil; *rnd_len = 0; 乱数データが取得できなかったことを示すために、ポインタをnilに、長さを0に設定します。これは、以前の挙動と同じですが、明示的にエラーハンドリングが行われています。

  5. ファイルディスクリプタのクローズ: runtime·close(fd); 乱数データの読み取りが完了した後、オープンした/dev/randomのファイルディスクリプタをruntime·close関数で閉じます。これは、リソースリークを防ぐための重要なステップです。

この変更により、GoランタイムはPlan 9環境においても、システムが提供する高品質な乱数源を適切に利用できるようになり、ハッシュテーブルのシード生成などの内部処理の堅牢性が向上します。

コアとなるコードの変更箇所

変更はsrc/pkg/runtime/os_plan9.cファイル内のruntime·get_random_data関数に集中しています。

--- a/src/pkg/runtime/os_plan9.c
+++ b/src/pkg/runtime/os_plan9.c
@@ -102,8 +102,18 @@ runtime·crash(void)
 void
 runtime·get_random_data(byte **rnd, int32 *rnd_len)
 {
-	*rnd = nil;
-	*rnd_len = 0;
+	static byte random_data[HashRandomBytes];
+	int32 fd;
+
+	fd = runtime·open("/dev/random", 0 /* O_RDONLY */, 0);
+	if(runtime·read(fd, random_data, HashRandomBytes) == HashRandomBytes) {
+		*rnd = random_data;
+		*rnd_len = HashRandomBytes;
+	} else {
+		*rnd = nil;
+		*rnd_len = 0;
+	}
+	runtime·close(fd);
 }

コアとなるコードの解説

runtime·get_random_data関数は、Goランタイムが外部から乱数データを取得するためのインターフェースです。この関数は、乱数データへのポインタ(rnd)と、そのデータの長さ(rnd_len)を引数として受け取ります。

変更前は、この関数は単に*rndnilに、*rnd_len0に設定するだけで、乱数データを一切提供していませんでした。これは、Plan 9環境では乱数源がない、あるいは利用できないという仮定に基づいていたと考えられます。

変更後は、以下の手順で乱数データを取得しようとします。

  1. static byte random_data[HashRandomBytes]; HashRandomBytesバイト分の静的バッファを確保します。このバッファは、/dev/randomから読み取った乱数データを一時的に保持するために使用されます。staticであるため、この関数が複数回呼び出されても、同じメモリ領域が再利用されます。

  2. int32 fd; ファイルディスクリプタを格納するための変数fdを宣言します。

  3. fd = runtime·open("/dev/random", 0 /* O_RDONLY */, 0); Plan 9の/dev/randomデバイスを読み取り専用モードでオープンします。0 /* O_RDONLY */は、読み取り専用フラグが数値の0であることを示しています。

  4. if(runtime·read(fd, random_data, HashRandomBytes) == HashRandomBytes) { ... } オープンした/dev/randomからHashRandomBytes分のデータをrandom_dataバッファに読み取ります。runtime·readは、実際に読み取れたバイト数を返します。もし読み取れたバイト数がHashRandomBytesと完全に一致すれば、必要な乱数データがすべて取得できたと判断します。

  5. 成功時の処理: *rnd = random_data; *rnd_len = HashRandomBytes; 読み取りが成功した場合、random_dataバッファへのポインタをrndに、読み取ったバイト数をrnd_lenに設定し、呼び出し元に乱数データを提供します。

  6. 失敗時の処理: else { *rnd = nil; *rnd_len = 0; } 読み取りが失敗した場合(例えば、/dev/randomが存在しない、読み取りエラーが発生した、または要求されたバイト数を読み取れなかった場合)、以前と同様にnil0を返し、乱数データが取得できなかったことを示します。

  7. runtime·close(fd); 最後に、オープンしたファイルディスクリプタfdを閉じます。これは、システムリソースを適切に解放するために不可欠です。

この変更により、GoランタイムはPlan 9環境でも、よりセキュアで予測不可能なハッシュシードを生成できるようになり、アプリケーションの堅牢性が向上します。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード(src/pkg/runtime/os_plan9.c
  • Go言語のコミット履歴
  • Plan 9のドキュメント(/dev/randomの存在確認のため)
  • /dev/random/dev/urandomに関する一般的なオペレーティングシステムの知識
  • ハッシュテーブルのシードとセキュリティに関する一般的な情報
  • Goのハッシュシードに関する議論(必要に応じて検索)
    • 例: "Go hash seed security" などで検索すると、関連する情報が見つかる可能性があります。
  • Go CL 43420045: https://golang.org/cl/43420045 (コミットメッセージに記載されているChange Listへのリンク)
    • このCLページには、コミットに関する詳細な議論やレビューコメントが含まれている場合があります。
    • このCLページは現在アクセスできません。これは、GoプロジェクトがGoogle CodeからGitHubに移行したため、古いCL番号が直接GitHubのコミットにリンクされていないためです。しかし、コミットメッセージに記載されているため、当時の議論の参照元として重要です。# [インデックス 18164] ファイルの概要

このコミットは、GoランタイムにおけるPlan 9オペレーティングシステム向けの乱数データ取得メカニズムの修正に関するものです。具体的には、Plan 9環境で/dev/randomデバイスが存在し、そこからエントロピー(乱数性)を取得できることを認識し、その機能を利用するように変更されました。

コミット

commit c136197ca8a536b6fd93f0ee2a7de6c6541c0124
Author: Jeff Sickel <jas@corpus-callosum.com>
Date:   Sat Jan 4 10:53:22 2014 -0800

    runtime: plan 9 does have /dev/random
    
    R=golang-codereviews, r, aram
    CC=0intro, golang-codereviews, rsc
    https://golang.org/cl/43420045

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/c136197ca8a536b6fd93f0ee2a7de6c6541c0124

元コミット内容

このコミットは、Goランタイムのos_plan9.cファイルにおいて、runtime·get_random_data関数が常にnil0を返していた(つまり、乱数データを取得しない)挙動を修正するものです。

変更前:

void
runtime·get_random_data(byte **rnd, int32 *rnd_len)
{
	*rnd = nil;
	*rnd_len = 0;
}

変更後:

void
runtime·get_random_data(byte **rnd, int32 *rnd_len)
{
	static byte random_data[HashRandomBytes];
	int32 fd;

	fd = runtime·open("/dev/random", 0 /* O_RDONLY */, 0);
	if(runtime·read(fd, random_data, HashRandomBytes) == HashRandomBytes) {
		*rnd = random_data;
		*rnd_len = HashRandomBytes;
	} else {
		*rnd = nil;
		*rnd_len = 0;
	}
	runtime·close(fd);
}

変更の背景

Goランタイムは、ハッシュテーブルのシード値など、様々な内部処理で乱数データ(エントロピー)を必要とします。これらの乱数データは、セキュリティ上の理由(例:ハッシュ衝突攻撃の緩和)や、予測不可能な動作の保証のために、高品質な乱数源から取得されるべきです。

以前のGoランタイムのPlan 9実装では、runtime·get_random_data関数が乱数データを取得する処理を実装しておらず、常に空のデータを返していました。これは、Plan 9オペレーティングシステムには/dev/randomのような乱数デバイスが存在しない、あるいは利用できないという誤解に基づいていた可能性があります。

このコミットの背景には、「Plan 9にも/dev/randomが存在し、利用可能である」という認識の更新があります。これにより、GoランタイムがPlan 9環境でも適切な乱数源からエントロピーを取得できるようになり、ハッシュテーブルのシード値などの生成において、より堅牢な乱数性を提供できるようになります。

前提知識の解説

Plan 9 from Bell Labs

Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Unixの設計思想をさらに推し進め、すべてのリソース(ファイル、デバイス、ネットワーク接続など)をファイルシステムとして表現するという「すべてはファイルである」という原則を徹底しています。これにより、システム内のあらゆる要素がファイル操作を通じてアクセス・制御できるようになっています。

/dev/random/dev/urandom

Unix系システム(およびPlan 9)において、/dev/random/dev/urandomは、カーネルが提供する擬似乱数生成器(PRNG)のインターフェースです。これらは、システムのエントロピー源(キーボード入力、マウスの動き、ディスクI/O、ネットワークイベントなど)から収集されたノイズを基に、高品質な乱数を生成します。

  • /dev/random: ブロッキング乱数源です。要求されたエントロピーが十分に蓄積されていない場合、エントロピーが利用可能になるまで読み出しをブロックします。これにより、常に高品質な乱数(真の乱数に近い)が保証されますが、エントロピーが枯渇するとシステムのパフォーマンスに影響を与える可能性があります。主に暗号鍵の生成など、高いセキュリティが要求される場面で使用されます。
  • /dev/urandom: 非ブロッキング乱数源です。エントロピーが不足している場合でも、利用可能なエントロピーから生成された乱数(擬似乱数)を返します。エントロピーが枯渇してもブロックしないため、パフォーマンスへの影響は少ないですが、エントロピーが枯渇した状態では予測可能性が高まる可能性があります。一般的な用途(セッションID、ソルトなど)で広く使用されます。

このコミットでは/dev/randomが使用されており、これはGoランタイムがハッシュシードなどの重要な用途に、より高品質な乱数を求めていることを示唆しています。

HashRandomBytes

HashRandomBytesは、Goランタイム内部で定義されている定数で、ハッシュテーブルのシード値として使用される乱数データのバイト数を指定します。この値は、ハッシュ衝突攻撃に対する耐性を高めるために、十分な長さの乱数シードを確保するために用いられます。具体的な値はGoのバージョンによって異なる場合がありますが、通常は数バイトから数十バイトの範囲です。このコミットでは、HashRandomBytes分のデータを/dev/randomから読み取ろうとしています。

技術的詳細

このコミットの技術的な核心は、GoランタイムがPlan 9環境で乱数データを取得する際の、より適切なシステムコールとファイル操作の利用にあります。

  1. 乱数データバッファの確保: static byte random_data[HashRandomBytes]; HashRandomBytesのサイズを持つ静的なバイト配列random_dataが宣言されます。staticキーワードにより、この配列は関数の呼び出し間でその内容を保持し、一度初期化されれば再利用されます。これは、乱数データを格納するための一時的なバッファとして機能します。

  2. /dev/randomのオープン: fd = runtime·open("/dev/random", 0 /* O_RDONLY */, 0); runtime·open関数は、Goランタイムが提供するシステムコールラッパーで、指定されたパスのファイルをオープンします。ここでは/dev/randomを読み取り専用モード(O_RDONLYは0に相当)でオープンしています。ファイルディスクリプタ(fd)が返されます。

  3. 乱数データの読み取り: if(runtime·read(fd, random_data, HashRandomBytes) == HashRandomBytes) { ... } runtime·read関数は、指定されたファイルディスクリプタからデータを読み取ります。ここでは、オープンした/dev/randomからHashRandomBytes分のデータをrandom_dataバッファに読み取ろうとしています。 読み取りが成功し、要求されたバイト数(HashRandomBytes)と実際に読み取れたバイト数が一致した場合、乱数データが正常に取得できたと判断されます。

  4. 乱数データのポインタと長さの設定: 読み取りが成功した場合: *rnd = random_data; *rnd_len = HashRandomBytes; 呼び出し元に、取得した乱数データが格納されているrandom_dataバッファへのポインタと、そのデータの長さ(HashRandomBytes)を渡します。

    読み取りが失敗した場合(例えば、/dev/randomが存在しない、読み取りエラーが発生した、または要求されたバイト数を読み取れなかった場合): *rnd = nil; *rnd_len = 0; 乱数データが取得できなかったことを示すために、ポインタをnilに、長さを0に設定します。これは、以前の挙動と同じですが、明示的にエラーハンドリングが行われています。

  5. ファイルディスクリプタのクローズ: runtime·close(fd); 乱数データの読み取りが完了した後、オープンした/dev/randomのファイルディスクリプタをruntime·close関数で閉じます。これは、リソースリークを防ぐための重要なステップです。

この変更により、GoランタイムはPlan 9環境においても、システムが提供する高品質な乱数源を適切に利用できるようになり、ハッシュテーブルのシード生成などの内部処理の堅牢性が向上します。

コアとなるコードの変更箇所

変更はsrc/pkg/runtime/os_plan9.cファイル内のruntime·get_random_data関数に集中しています。

--- a/src/pkg/runtime/os_plan9.c
+++ b/src/pkg/runtime/os_plan9.c
@@ -102,8 +102,18 @@ runtime·crash(void)
 void
 runtime·get_random_data(byte **rnd, int32 *rnd_len)
 {
-	*rnd = nil;
-	*rnd_len = 0;
+	static byte random_data[HashRandomBytes];
+	int32 fd;
+
+	fd = runtime·open("/dev/random", 0 /* O_RDONLY */, 0);
+	if(runtime·read(fd, random_data, HashRandomBytes) == HashRandomBytes) {
+		*rnd = random_data;
+		*rnd_len = HashRandomBytes;
+	} else {
+		*rnd = nil;
+		*rnd_len = 0;
+	}
+	runtime·close(fd);
 }

コアとなるコードの解説

runtime·get_random_data関数は、Goランタイムが外部から乱数データを取得するためのインターフェースです。この関数は、乱数データへのポインタ(rnd)と、そのデータの長さ(rnd_len)を引数として受け取ります。

変更前は、この関数は単に*rndnilに、*rnd_len0に設定するだけで、乱数データを一切提供していませんでした。これは、Plan 9環境では乱数源がない、あるいは利用できないという仮定に基づいていたと考えられます。

変更後は、以下の手順で乱数データを取得しようとします。

  1. static byte random_data[HashRandomBytes]; HashRandomBytesバイト分の静的バッファを確保します。このバッファは、/dev/randomから読み取った乱数データを一時的に保持するために使用されます。staticであるため、この関数が複数回呼び出されても、同じメモリ領域が再利用されます。

  2. int32 fd; ファイルディスクリプタを格納するための変数fdを宣言します。

  3. fd = runtime·open("/dev/random", 0 /* O_RDONLY */, 0); Plan 9の/dev/randomデバイスを読み取り専用モードでオープンします。0 /* O_RDONLY */は、読み取り専用フラグが数値の0であることを示しています。

  4. if(runtime·read(fd, random_data, HashRandomBytes) == HashRandomBytes) { ... } オープンした/dev/randomからHashRandomBytes分のデータをrandom_dataバッファに読み取ります。runtime·readは、実際に読み取れたバイト数を返します。もし読み取れたバイト数がHashRandomBytesと完全に一致すれば、必要な乱数データがすべて取得できたと判断します。

  5. 成功時の処理: *rnd = random_data; *rnd_len = HashRandomBytes; 読み取りが成功した場合、random_dataバッファへのポインタをrndに、読み取ったバイト数をrnd_lenに設定し、呼び出し元に乱数データを提供します。

  6. 失敗時の処理: else { *rnd = nil; *rnd_len = 0; } 読み取りが失敗した場合(例えば、/dev/randomが存在しない、読み取りエラーが発生した、または要求されたバイト数を読み取れなかった場合)、以前と同様にnil0を返し、乱数データが取得できなかったことを示します。

  7. runtime·close(fd); 最後に、オープンしたファイルディスクリプタfdを閉じます。これは、システムリソースを適切に解放するために不可欠です。

この変更により、GoランタイムはPlan 9環境でも、よりセキュアで予測不可能なハッシュシードを生成できるようになり、アプリケーションの堅牢性が向上します。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード(src/pkg/runtime/os_plan9.c
  • Go言語のコミット履歴
  • Plan 9のドキュメント(/dev/randomの存在確認のため)
  • /dev/random/dev/urandomに関する一般的なオペレーティングシステムの知識
  • ハッシュテーブルのシードとセキュリティに関する一般的な情報
  • Goのハッシュシードに関する議論(必要に応じて検索)
    • 例: "Go hash seed security" などで検索すると、関連する情報が見つかる可能性があります。
  • Go CL 43420045: https://golang.org/cl/43420045 (コミットメッセージに記載されているChange Listへのリンク)
    • このCLページには、コミットに関する詳細な議論やレビューコメントが含まれている場合があります。
    • このCLページは現在アクセスできません。これは、GoプロジェクトがGoogle CodeからGitHubに移行したため、古いCL番号が直接GitHubのコミットにリンクされていないためです。しかし、コミットメッセージに記載されているため、当時の議論の参照元として重要です。