[インデックス 10947] ファイルの概要
このコミットは、Go言語の標準ライブラリ crypto/x509
パッケージにおける CertPool
型の findVerifiedParents
メソッドが、nil
レシーバで呼び出された際にクラッシュする可能性があったバグを修正するものです。具体的には、メソッドの冒頭でレシーバが nil
であるかどうかのチェックを追加し、nil
の場合は早期に処理を終了するように変更されています。また、この修正が正しく機能することを確認するための新しいテストケースも追加されています。
コミット
commit 71f0fb77602701bf3e3f6efd3aa1be5d42a64458
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Wed Dec 21 10:49:35 2011 -0800
crypto/x509: don't crash with nil receiver in accessor method
Fixes #2600
R=golang-dev, agl, rsc
CC=golang-dev
https://golang.org/cl/5500064
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/71f0fb77602701bf3e3f6efd3aa1be5d42a64458
元コミット内容
crypto/x509: don't crash with nil receiver in accessor method
このコミットは、crypto/x509
パッケージ内のアクセサメソッドが nil
レシーバで呼び出された際にクラッシュしないように修正します。
変更の背景
Go言語では、メソッドはポインタレシーバ (*T
) または値レシーバ (T
) を持つことができます。ポインタレシーバを持つメソッドは、レシーバが nil
である場合でも呼び出すことが可能です。しかし、メソッド内で nil
レシーバのフィールドにアクセスしようとすると、ランタイムパニック(nil
ポインタデリファレンス)が発生し、プログラムがクラッシュします。
このコミット以前の crypto/x509
パッケージの CertPool
型の findVerifiedParents
メソッドは、ポインタレシーバ *CertPool
を持っていました。しかし、メソッドの内部でレシーバ s
が nil
である可能性を考慮していなかったため、もし s
が nil
の状態でこのメソッドが呼び出されると、内部で nil
レシーバのフィールドにアクセスしようとしてパニックが発生する脆弱性がありました。
この問題は、GoのIssue #2600として報告されており、このコミットはその問題を解決するために作成されました。特に、証明書検証の際に CertPool
が nil
になるようなエッジケースで、予期せぬクラッシュを引き起こす可能性がありました。
前提知識の解説
Go言語のレシーバとメソッド
Go言語において、メソッドは特定の型に関連付けられた関数です。メソッドはレシーバ(receiver)と呼ばれる特別な引数を持ち、これによりそのメソッドがどの型の値に対して操作を行うかを指定します。レシーバには「値レシーバ」と「ポインタレシーバ」の2種類があります。
- 値レシーバ (
func (t T) MethodName(...)
): メソッドが呼び出されると、レシーバの値のコピーが作成されます。メソッド内でレシーバの値を変更しても、元の値には影響しません。 - ポインタレシーバ (
func (t *T) MethodName(...)
): メソッドが呼び出されると、レシーバのポインタのコピーが作成されます。メソッド内でポインタを通じてレシーバの値を変更すると、元の値も変更されます。また、ポインタレシーバを持つメソッドは、レシーバがnil
であるポインタに対しても呼び出すことができます。これはGoの設計上の特徴であり、nil
レシーバに対する振る舞いを定義することで、より柔軟なAPI設計が可能になります。
nil
の概念とGoにおけるnil
ポインタ
nil
はGo言語におけるゼロ値の一つで、ポインタ、インターフェース、マップ、スライス、チャネルなどの参照型が何も指していない状態を表します。ポインタが nil
である場合、それは有効なメモリアドレスを指していません。nil
ポインタをデリファレンス(つまり、nil
ポインタが指す先の値にアクセスしようとすること)しようとすると、ランタイムパニックが発生し、プログラムが異常終了します。
crypto/x509
パッケージ
crypto/x509
パッケージは、Go言語の標準ライブラリの一部であり、X.509証明書とPKIX(Public Key Infrastructure for X.509)に関する機能を提供します。これには、証明書の解析、検証、証明書署名要求(CSR)の生成などが含まれます。ウェブサイトのHTTPS通信や、セキュアな通信プロトコルにおいて、サーバーやクライアントの身元を確認するためにX.509証明書が広く利用されています。
CertPool
型
CertPool
は crypto/x509
パッケージ内で定義されている型で、信頼されたルート証明書や中間証明書を格納するためのコレクションです。証明書の検証プロセスにおいて、与えられた証明書がこのプール内のいずれかの証明書によって署名されているか、または信頼されたルート証明書に連なるチェーンの一部であるかを確認するために使用されます。
findVerifiedParents
メソッド
CertPool
型の findVerifiedParents
メソッドは、特定の証明書 (cert
) の親となる可能性のある証明書を CertPool
内から探し出す役割を担います。これは、証明書チェーンを構築し、最終的にルート証明書まで辿ることで、証明書の信頼性を検証するプロセスの一部です。
技術的詳細
このコミットの技術的な核心は、crypto/x509/cert_pool.go
ファイル内の findVerifiedParents
メソッドに nil
レシーバチェックを追加した点にあります。
元のコードでは、findVerifiedParents
メソッドは *CertPool
型のレシーバ s
を受け取ります。Goの仕様上、ポインタレシーバを持つメソッドは、そのポインタが nil
であっても呼び出すことができます。しかし、メソッドの内部で s
が nil
であるにもかかわらず、s
のフィールド(例えば s.certs
や s.bySubject
など)にアクセスしようとすると、nil
ポインタデリファレンスが発生し、プログラムがクラッシュします。
このコミットでは、findVerifiedParents
メソッドの冒頭に以下のコードが追加されました。
if s == nil {
return
}
このシンプルな nil
チェックにより、s
が nil
の場合は、メソッドがそれ以上処理を進めることなく、空の parents
スライスを返して安全に終了するようになります。これにより、nil
レシーバによるクラッシュが防止されます。
さらに、src/pkg/crypto/x509/verify_test.go
に新しいテストケースが追加されました。このテストケースは、CertPool
が nil
の状態で証明書検証プロセスが実行された場合に、クラッシュが発生しないことを保証します。具体的には、verifyTest
構造体に nilRoots
フィールドが追加され、これが true
の場合に opts.Roots
(CertPool
のインスタンス)が意図的に nil
に設定されるようになっています。このテストは、nil
レシーバの修正が正しく機能し、将来的に同様の回帰バグが発生しないための安全網となります。
コアとなるコードの変更箇所
diff --git a/src/pkg/crypto/x509/cert_pool.go b/src/pkg/crypto/x509/cert_pool.go
index adc7f9bc6d..5a0a87678e 100644
--- a/src/pkg/crypto/x509/cert_pool.go
+++ b/src/pkg/crypto/x509/cert_pool.go
@@ -28,6 +28,9 @@ func NewCertPool() *CertPool {
// given certificate. If no such certificate can be found or the signature
// doesn\'t match, it returns nil.\n func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int) {\n+\tif s == nil {\n+\t\treturn\n+\t}\n \tvar candidates []int
\n \tif len(cert.AuthorityKeyId) > 0 {\ndiff --git a/src/pkg/crypto/x509/verify_test.go b/src/pkg/crypto/x509/verify_test.go
index df5443023f..2016858307 100644
--- a/src/pkg/crypto/x509/verify_test.go
+++ b/src/pkg/crypto/x509/verify_test.go
@@ -19,6 +19,7 @@ type verifyTest struct {\n \troots []string\n \tcurrentTime int64\n \tdnsName string\n+\tnilRoots bool\n \n \terrorCallback func(*testing.T, int, error) bool\n \texpectedChains [][]string\n@@ -45,6 +46,14 @@ var verifyTests = []verifyTest{\n \n \t\terrorCallback: expectHostnameError,\n \t},\n+\t{\n+\t\tleaf: googleLeaf,\n+\t\tintermediates: []string{thawteIntermediate},\n+\t\tnilRoots: true, // verifies that we don\'t crash\n+\t\tcurrentTime: 1302726541,\n+\t\tdnsName: \"www.google.com\",\n+\t\terrorCallback: expectAuthorityUnknown,\n+\t},\n \t{\n \t\tleaf: googleLeaf,\n \t\tintermediates: []string{thawteIntermediate},\n@@ -136,6 +145,9 @@ func TestVerify(t *testing.T) {\n \t\t\tDNSName: test.dnsName,\n \t\t\tCurrentTime: time.Unix(test.currentTime, 0),\n \t\t}\n+\t\tif test.nilRoots {\n+\t\t\topts.Roots = nil\n+\t\t}\n \n \t\tfor j, root := range test.roots {\n \t\t\tok := opts.Roots.AppendCertsFromPEM([]byte(root))\n```
## コアとなるコードの解説
### `src/pkg/crypto/x509/cert_pool.go` の変更
`func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int)` メソッドの冒頭に以下の3行が追加されました。
```go
if s == nil {
return
}
これは、メソッドのレシーバ s
が nil
であるかどうかをチェックするガード句です。もし s
が nil
であれば、それ以上メソッド内の処理(nil
レシーバのフィールドへのアクセスなど)を実行することなく、空の parents
スライスを返して安全にメソッドを終了します。これにより、nil
ポインタデリファレンスによるランタイムパニックが回避されます。
src/pkg/crypto/x509/verify_test.go
の変更
-
verifyTest
構造体に新しいフィールドnilRoots bool
が追加されました。このフィールドは、テストケースでCertPool
をnil
に設定するかどうかを制御します。type verifyTest struct { // ... nilRoots bool // ... }
-
verifyTests
スライスに新しいテストケースが追加されました。このテストケースは、nilRoots: true
を設定することで、CertPool
がnil
の状態で検証が試みられた場合にクラッシュしないことを検証します。{ leaf: googleLeaf, intermediates: []string{thawteIntermediate}, nilRoots: true, // verifies that we don't crash currentTime: 1302726541, dnsName: "www.google.com", errorCallback: expectAuthorityUnknown, },
このテストケースでは、
nilRoots
がtrue
に設定されており、コメントで「クラッシュしないことを検証する」と明記されています。errorCallback: expectAuthorityUnknown
は、nil
のCertPool
では当然ながら認証局が不明であるというエラーが期待されることを示しています。 -
TestVerify
関数内で、test.nilRoots
がtrue
の場合にopts.Roots
をnil
に設定するロジックが追加されました。if test.nilRoots { opts.Roots = nil }
これにより、新しいテストケースが実行される際に、実際に
CertPool
がnil
の状態でfindVerifiedParents
メソッドが呼び出される状況が再現され、修正が正しく機能するかどうかが検証されます。
これらの変更により、crypto/x509
パッケージはより堅牢になり、nil
レシーバのケースでも安全に動作するようになりました。
関連リンク
- Gerrit Change-ID: https://golang.org/cl/5500064
- Go Issue (参照): #2600 (このコミットが修正したGoの内部トラッカーのIssue番号。直接のGitHubリンクは特定できませんでした。)
参考にした情報源リンク
- Go言語の公式ドキュメント (Go言語のレシーバ、メソッド、
nil
の概念について) crypto/x509
パッケージのGoDoc (Go言語の標準ライブラリのドキュメント)- Go言語のソースコード (特に
src/crypto/x509/cert_pool.go
およびsrc/crypto/x509/verify_test.go
の変更履歴)