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

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

このコミットは、Go言語の標準ライブラリである crypto/rand パッケージ内の util.go ファイルに対する変更です。具体的には、Int 関数の引数検証とエラーハンドリングの改善が行われています。

コミット

commit b08a3164c0a10b608db0e9fafcdc3c9168e3cffe
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date:   Wed Apr 17 23:21:15 2013 -0700

    crypto/rand: better panic message for invalid Int argument.
    
    Also document the panic to be consistent with math/rand.
    
    Fixes #5187.
    
    R=golang-dev, dave, bradfitz, r
    CC=golang-dev
    https://golang.org/cl/8303043

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

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

元コミット内容

crypto/rand: better panic message for invalid Int argument. Also document the panic to be consistent with math/rand. Fixes #5187.

このコミットは、crypto/rand パッケージの Int 関数において、無効な引数(max が0以下)が渡された場合のパニックメッセージを改善し、math/rand パッケージとの一貫性を保つためにパニックの発生をドキュメントに追加することを目的としています。また、GoのIssue #5187 を修正します。

変更の背景

この変更の背景には、Go言語の標準ライブラリにおける乱数生成関数の堅牢性と一貫性の向上が挙げられます。

crypto/rand パッケージは、暗号学的に安全な乱数を生成するために使用されます。Int 関数は、指定された範囲 [0, max) で一様乱数を生成する重要な関数です。しかし、max 引数が0以下の場合、数学的にこの範囲は無効となり、関数の意図する動作が不可能になります。

コミットメッセージにある Fixes #5187 は、この変更が特定のバグまたは改善要求に対応していることを示しています。GoのIssue #5187 を確認すると、crypto/rand.Intmax が0以下の場合にパニックを起こすものの、そのメッセージが不明瞭であり、また math/rand.Int との動作の一貫性がないという問題が提起されていました。

具体的には、math/rand.Intn (Go 1.0時点では math/rand.Int) は n <= 0 の場合にパニックを起こし、その旨がドキュメントに明記されていました。一方、crypto/rand.Int は同様の状況でパニックを起こすものの、そのパニックメッセージが「runtime error: division by zero」のような、原因を特定しにくいものであったり、ドキュメントにパニックの条件が明記されていなかったりしました。

この不一致と不明瞭さを解消し、ユーザーがより予測可能でデバッグしやすいコードを書けるようにするために、今回の変更が導入されました。

前提知識の解説

crypto/rand パッケージ

crypto/rand パッケージは、暗号学的に安全な乱数を生成するためのGo言語の標準ライブラリです。通常の乱数(math/rand パッケージで提供されるもの)とは異なり、予測不可能性が非常に高く、セキュリティが要求される場面(鍵生成、セッションID生成など)で利用されます。

math/rand パッケージ

math/rand パッケージは、擬似乱数を生成するためのGo言語の標準ライブラリです。シード値に基づいて決定論的に乱数を生成するため、暗号学的な用途には適していません。しかし、一般的なシミュレーションやゲームなどで広く利用されます。

big.Int

math/big パッケージで提供される big.Int 型は、任意精度の整数を扱うための型です。Goの組み込み整数型(int, int64 など)では表現できない非常に大きな整数を扱う際に使用されます。crypto/rand.Int 関数は、この big.Int 型を引数として受け取り、同じく big.Int 型の乱数を返します。

panicrecover

Go言語における panic は、プログラムの実行を中断させる回復不可能なエラーを示すメカニズムです。通常、予期せぬプログラミングエラー(例: nilポインタ参照、配列の範囲外アクセス)や、プログラムが続行できないような致命的な状況で発生します。panic が発生すると、現在のゴルーチンの通常の実行フローは停止し、遅延関数(defer)が実行され、その後呼び出し元の関数へとスタックを巻き戻していきます。最終的に、recover 関数が呼び出されない限り、プログラム全体がクラッシュします。

今回のケースでは、Int 関数の max 引数が無効な値であることは、関数の契約違反と見なされ、プログラムがその後の処理を安全に続行できないため、panic が適切なエラーハンドリングメカニズムとして選択されています。

max.Sign() メソッド

big.Int 型の Sign() メソッドは、その数値の符号を返します。

  • x.Sign() == -1 ならば x < 0
  • x.Sign() == 0 ならば x == 0
  • x.Sign() == 1 ならば x > 0

このコミットでは max.Sign() <= 0 という条件でパニックを発生させており、これは max が0以下の場合(負の数または0)を意味します。

技術的詳細

crypto/rand.Int(rand io.Reader, max *big.Int) 関数は、暗号学的に安全な乱数ジェネレータ rand から読み取り、[0, max) の範囲で一様な乱数を *big.Int 型で返します。

この関数が正しく機能するためには、max が正の整数である必要があります。もし max が0以下の場合、[0, max) という範囲は数学的に意味をなさず、乱数を生成することができません。

変更前は、max が0以下の場合に、内部の計算(例えば、max を使用したビット長計算やモジュロ演算)が不正な結果を引き起こし、結果として「runtime error: division by zero」のような、直接的ではないパニックメッセージが発生していました。これは、開発者にとって問題の原因を特定しにくいものでした。

今回の変更では、関数の冒頭で明示的に max の値をチェックするようになりました。

if max.Sign() <= 0 {
    panic("crypto/rand: argument to Int is <= 0")
}

このチェックにより、max が0以下であるという無効な状態が早期に検出され、より具体的で分かりやすいパニックメッセージ "crypto/rand: argument to Int is <= 0" が出力されるようになります。これにより、開発者は問題の原因を迅速に特定し、修正することができます。

また、この変更は math/rand.Intn (当時の math/rand.Int) の動作と一貫性を持たせるものです。math/rand.Intn(n int)n <= 0 の場合にパニックを起こすことがドキュメントに明記されており、同様の引数制約を持つ crypto/rand.Int も同様の振る舞いをすることで、Goの乱数生成API全体の一貫性が向上します。

ドキュメントの更新も重要です。関数のコメントに It panics if max <= 0. という記述が追加されたことで、ユーザーは関数の振る舞いを事前に理解し、無効な引数を渡すことを避けることができます。これは、APIの使いやすさと堅牢性を高める上で非常に重要です。

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

変更は src/pkg/crypto/rand/util.go ファイルの Int 関数内で行われました。

--- a/src/pkg/crypto/rand/util.go
+++ b/src/pkg/crypto/rand/util.go
@@ -100,8 +100,11 @@ func Prime(rand io.Reader, bits int) (p *big.Int, err error) {
 	}
 }
 
-// Int returns a uniform random value in [0, max).
+// Int returns a uniform random value in [0, max). It panics if max <= 0.
 func Int(rand io.Reader, max *big.Int) (n *big.Int, err error) {
+	if max.Sign() <= 0 {
+		panic("crypto/rand: argument to Int is <= 0")
+	}
 	k := (max.BitLen() + 7) / 8
 
 	// b is the number of bits in the most significant byte of max.

具体的には以下の変更が行われました。

  1. 関数のコメントの変更: - // Int returns a uniform random value in [0, max). + // Int returns a uniform random value in [0, max). It panics if max <= 0. Int 関数のドキュメントに、max が0以下の場合にパニックが発生することが明記されました。

  2. 引数チェックの追加: + if max.Sign() <= 0 { + panic("crypto/rand: argument to Int is <= 0") + } Int 関数の冒頭に、max の符号をチェックする条件文が追加されました。max.Sign() <= 0max が負の数または0であることを意味します。この条件が真の場合、"crypto/rand: argument to Int is <= 0" というメッセージと共にパニックが発生します。

コアとなるコードの解説

追加されたコードブロックは、Int 関数の引数 max の有効性を早期に検証するためのものです。

if max.Sign() <= 0 {
    panic("crypto/rand: argument to Int is <= 0")
}
  • max.Sign(): big.Int 型の max の符号を返します。
    • -1: max が負の数
    • 0: max がゼロ
    • 1: max が正の数
  • max.Sign() <= 0: この条件は、max が負の数であるか、またはゼロである場合に真となります。
  • panic("crypto/rand: argument to Int is <= 0"): 条件が真の場合、指定された文字列をパニックメッセージとしてプログラムの実行を中断します。

この変更の主な目的は以下の通りです。

  1. 明確なエラーメッセージ: 以前は、無効な max 値が渡された場合に、内部の計算エラー(例: ゼロ除算)に起因する不明瞭なパニックメッセージが発生する可能性がありました。この変更により、問題が引数の不正な値にあることが明確に示されるようになります。
  2. 早期のエラー検出: 関数の実行が始まる直後に引数の有効性をチェックすることで、無効な状態での後続の計算を防ぎ、デバッグを容易にします。
  3. APIの一貫性: math/rand.Intn と同様に、crypto/rand.Int も無効な範囲が指定された場合にパニックを起こすことで、Goの乱数生成API全体の一貫性が保たれます。これは、開発者が異なる乱数パッケージを使用する際に、同様の期待を持つことができるようにするために重要です。
  4. ドキュメントとの同期: 関数のコメントにパニックの条件が明記されたことで、コードの振る舞いがドキュメントと一致し、ユーザーがAPIを正しく理解し、誤用を避けるのに役立ちます。

このシンプルなチェックの追加により、crypto/rand.Int 関数の堅牢性、使いやすさ、およびGo標準ライブラリ全体の一貫性が大幅に向上しました。

関連リンク

参考にした情報源リンク