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

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

このコミットは、Go言語の標準ライブラリ crypto/x509 パッケージにおけるWindows環境での証明書コンテキストの解放に関するバグ修正です。具体的には、createStoreContext 関数内で syscall.CertFreeCertificateContext(ctx) の呼び出しが、その前の syscall.CertAddCertificateContextToStore のエラーチェックの後に配置されていたため、エラーが発生した場合に ctx が解放されない可能性があった問題を修正しています。

コミット

commit 9fffe45c6551a495f00d5593e941f7f1b1b7e784
Author: Alex Brainman <alex.brainman@gmail.com>
Date:   Sat Mar 10 09:35:56 2012 +1100

    crypto/x509: do not forget to free cert context
    
    R=golang-dev, krautz, rsc
    CC=golang-dev
    https://golang.org/cl/5783059

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

https://github.com/golang/go/commit/9fffe45c6551a495f00d559e941f7f1b1b7e784

元コミット内容

crypto/x509: do not forget to free cert context

このコミットメッセージは、証明書コンテキストの解放を忘れないようにするという簡潔な内容です。これは、リソースリークの修正を示唆しています。

変更の背景

この変更の背景には、Windows APIを介して証明書ストアを操作する際に、リソースリークが発生する可能性があったという問題があります。createStoreContext 関数は、証明書をWindowsの証明書ストアに追加する役割を担っています。この関数内で、syscall.CertAddCertificateContextToStore の呼び出しが成功したかどうかにかかわらず、取得した証明書コンテキスト (ctx) を適切に解放する必要がありました。

しかし、元のコードでは syscall.CertAddCertificateContextToStore の呼び出しの直後にエラーチェックがあり、もしこの関数がエラーを返した場合、その後の syscall.CertFreeCertificateContext(ctx) の呼び出しがスキップされていました。これにより、ctx が解放されずにメモリリークやリソースリークが発生する可能性がありました。

このコミットは、このようなリソースリークを防ぐために、syscall.CertFreeCertificateContext(ctx) の呼び出しを、syscall.CertAddCertificateContextToStore のエラーチェックよりも前に移動させることで、ctx が常に解放されるように修正しています。

前提知識の解説

Go言語の crypto/x509 パッケージ

crypto/x509 パッケージは、Go言語でX.509証明書とPKIX (Public Key Infrastructure X.509) を扱うための機能を提供します。これには、証明書のパース、検証、署名、および証明書チェーンの構築などが含まれます。TLS/SSL通信やコード署名など、セキュリティが要求される多くの場面で利用されます。

Go言語の syscall パッケージ

syscall パッケージは、Goプログラムからオペレーティングシステム(OS)のシステムコールを直接呼び出すための低レベルなインターフェースを提供します。これにより、OS固有の機能(ファイルシステム操作、ネットワーク通信、プロセス管理など)にアクセスできます。Windows環境では、Win32 APIを呼び出すためにこのパッケージが使用されます。

Windowsの証明書ストアと CertContext

Windowsオペレーティングシステムは、証明書を管理するための「証明書ストア」という仕組みを持っています。これは、ユーザーやコンピュータの信頼されたルート証明書、個人証明書、中間証明書などを保存する場所です。

CertContext (Certificate Context) は、Windows APIにおいて証明書を表すデータ構造です。これは、証明書データ自体だけでなく、そのプロパティや拡張情報へのポインタを含みます。Windows APIを介して証明書を操作する際には、この CertContext オブジェクトを介して行われます。

CertAddCertificateContextToStore 関数

CertAddCertificateContextToStore は、指定された証明書コンテキストを証明書ストアに追加するWindows API関数です。この関数は、証明書ストアへの参照と、追加する証明書コンテキスト、および追加方法を指定するフラグ(例: CERT_STORE_ADD_ALWAYS)を引数に取ります。

CertFreeCertificateContext 関数

CertFreeCertificateContext は、CertContext オブジェクトが占有していたメモリやリソースを解放するためのWindows API関数です。Windows APIでは、多くの関数がリソース(メモリハンドル、ファイルハンドルなど)を割り当て、それらを使い終わったら明示的に解放する必要があります。これを怠ると、リソースリークが発生し、システムのパフォーマンス低下や不安定化につながる可能性があります。

技術的詳細

このコミットは、src/pkg/crypto/x509/root_windows.go ファイル内の createStoreContext 関数におけるリソース管理のロジックを修正しています。

元のコードでは、以下の順序で処理が行われていました。

  1. syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil) を呼び出し、証明書をストアに追加。
  2. 上記関数の戻り値 err をチェック。
    • err != nil の場合、エラーを返して関数を終了。
    • err == nil の場合、処理を続行。
  3. syscall.CertFreeCertificateContext(ctx) を呼び出し、ctx を解放。
  4. 上記関数の戻り値 err をチェック。
    • err != nil の場合、エラーを返して関数を終了。

このロジックの問題点は、ステップ2で syscall.CertAddCertificateContextToStore がエラーを返した場合、ステップ3の syscall.CertFreeCertificateContext(ctx) が実行されないことです。ctxCertAddCertificateContextToStore に渡される前に既に有効なコンテキストとして存在しているため、エラーが発生した場合でも、そのコンテキストが占有していたリソースは解放されるべきです。

修正後のコードでは、syscall.CertFreeCertificateContext(ctx) の呼び出しが syscall.CertAddCertificateContextToStore のエラーチェックよりも前に移動されました。これにより、CertAddCertificateContextToStore が成功したか失敗したかにかかわらず、ctx は常に解放されることが保証されます。

// 修正前
err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil)
if err != nil {
    return nil, err
}
err = syscall.CertFreeCertificateContext(ctx) // ここでエラーが発生すると、ctxが解放されない
if err != nil {
    return nil, err
}

// 修正後
err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil)
syscall.CertFreeCertificateContext(ctx) // エラーチェックの前に移動
if err != nil { // CertAddCertificateContextToStoreのエラーのみをチェック
    return nil, err
}
// CertFreeCertificateContextのエラーは無視されるか、別途処理されるべきだが、
// この修正ではCertAddCertificateContextToStoreのエラーパスのみを考慮している

この変更により、createStoreContext 関数がどのような状況で終了しても、ctx に関連付けられたリソースが適切にクリーンアップされるようになり、リソースリークが防止されます。

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

--- a/src/pkg/crypto/x509/root_windows.go
+++ b/src/pkg/crypto/x509/root_windows.go
@@ -45,11 +45,7 @@ func createStoreContext(leaf *Certificate, opts *VerifyOptions) (*syscall.CertCo
 			}
 
 			err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil)
-			if err != nil {
-				return nil, err
-			}
-
-			err = syscall.CertFreeCertificateContext(ctx)
+			syscall.CertFreeCertificateContext(ctx)
 			if err != nil {
 				return nil, err
 			}

コアとなるコードの解説

変更は src/pkg/crypto/x509/root_windows.go ファイルの createStoreContext 関数内で行われています。

元のコードでは、以下の5行が削除されています。

			if err != nil {
				return nil, err
			}

			err = syscall.CertFreeCertificateContext(ctx)

そして、以下の1行が追加されています。

			syscall.CertFreeCertificateContext(ctx)

これにより、syscall.CertAddCertificateContextToStore の呼び出しの直後に syscall.CertFreeCertificateContext(ctx) が無条件に呼び出されるようになりました。

元のコードでは、syscall.CertAddCertificateContextToStore がエラーを返した場合、そのエラーをチェックして関数を早期に終了していました。このとき、ctx は解放されませんでした。その後、syscall.CertFreeCertificateContext(ctx) の呼び出しがありましたが、これは CertAddCertificateContextToStore が成功した場合にのみ実行されるパスでした。

修正後は、syscall.CertAddCertificateContextToStore の呼び出しが完了した直後に、その結果が成功か失敗かにかかわらず、syscall.CertFreeCertificateContext(ctx) が呼び出されます。これにより、ctx が指すリソースは常に解放されることが保証されます。その後の if err != nil は、syscall.CertAddCertificateContextToStore から返されたエラーをチェックするためのものであり、CertFreeCertificateContext のエラーはここでは捕捉されなくなっています。これは、リソース解放のエラーよりも、証明書追加のエラーの方が上位の関心事であるという判断に基づいている可能性があります。

関連リンク

参考にした情報源リンク

  • Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
  • Go言語のコードレビューシステム (Gerrit): https://go.dev/cl/5783059 (コミットメッセージに記載されているCLリンク)
  • 一般的なリソースリークに関する情報 (プログラミングのベストプラクティス)
  • Windows APIのメモリ管理に関する情報