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

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

このコミットは、Go言語のsyscallパッケージ内のexec_plan9.goファイルに対する変更です。このファイルは、Plan 9オペレーティングシステム上でのプロセス実行(exec)に関連するシステムコールを扱うためのGo言語のインターフェースを提供します。具体的には、子プロセスの起動やエラーハンドリングのロジックが含まれています。

コミット

このコミットは、Plan 9環境におけるexecパッケージからのエラーメッセージが正しく終端されず、余分な文字(ガベージ)が付加されて表示される問題を修正します。具体的には、エラーバッファからGoの文字列を生成する際に、実際に読み取られたバイト数nを使用して文字列を正確に切り詰めることで、この問題を解決しています。

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

https://github.com/golang/go/commit/3e37720bcebefe87459a7dd8f41164c0e2cfa4bc

元コミット内容

syscall: terminate error string in exec package on Plan 9
Try to prevent messages like this:
        './pack' file does not exist
TBR=adonovan

LGTM=adonovan
R=adonovan
CC=golang-codereviews
https://golang.org/cl/66270043

変更の背景

この変更の背景には、Plan 9オペレーティングシステム上でGoプログラムが外部コマンドを実行(exec)した際に、エラーメッセージが期待通りに表示されないという問題がありました。具体的には、エラーメッセージの末尾に意味不明な文字(例: ``)が多数付加されてしまう現象が発生していました。これは、エラーメッセージを格納するバッファが、実際のメッセージよりも大きく確保されており、かつGoの文字列に変換する際にバッファ全体が使われてしまうために、バッファの未使用部分に含まれるゴミデータがそのまま表示されてしまうことが原因でした。

開発者は、ユーザーが正確なエラー情報を得られるように、この視覚的なノイズを取り除く必要がありました。

前提知識の解説

Plan 9

Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Unixの概念をさらに推し進め、すべてのリソース(ファイル、デバイス、ネットワーク接続など)をファイルシステムとして表現するという哲学を持っています。Go言語は、その設計思想の一部をPlan 9から継承しており、初期のGo開発者にはPlan 9の設計者が多く含まれていました。そのため、Goの標準ライブラリにはPlan 9固有のシステムコールを扱うためのコードが含まれています。

Go言語のsyscallパッケージ

syscallパッケージは、Goプログラムから基盤となるオペレーティングシステムのシステムコールに直接アクセスするための低レベルなインターフェースを提供します。これにより、ファイル操作、プロセス管理、ネットワーク通信など、OS固有の機能を利用できます。OSごとに実装が異なるため、syscallパッケージ内にはsyscall_linux.gosyscall_windows.go、そしてこのコミットで関連するsyscall_plan9.goのように、OS固有のファイルが存在します。

execシステムコール

execは、現在のプロセスを新しいプログラムで置き換えるシステムコールです。これにより、新しいプロセスを生成することなく、実行中のプログラムを別のプログラムに切り替えることができます。Go言語のos/execパッケージは、このexecシステムコールをより高レベルで抽象化して提供していますが、その内部ではsyscallパッケージの機能を利用しています。

C言語スタイルの文字列とGo言語の文字列

この問題の根源は、C言語スタイルの文字列とGo言語の文字列の扱いの違いにあります。

  • C言語スタイルの文字列: C言語では、文字列は文字の配列として扱われ、その終端はヌル文字(\0)によって示されます。文字列の長さを知るには、ヌル文字が見つかるまで配列を走査する必要があります。
  • Go言語の文字列: Go言語では、文字列は不変のバイトスライスであり、その長さは文字列自体に付随するメタデータとして管理されます。ヌル終端は必要ありません。

syscallパッケージがOSからエラーメッセージを受け取る際、多くの場合、それはC言語スタイルのヌル終端された文字列としてバッファに書き込まれます。しかし、Goの文字列に変換する際に、バッファの実際の有効なデータ長を考慮せずにバッファ全体を文字列として解釈してしまうと、ヌル文字以降の未使用領域に含まれるゴミデータも文字列の一部として扱われてしまい、結果としてガベージ文字が表示されることになります。

技術的詳細

このコミットが修正している問題は、forkExec関数内で子プロセスがエラーを返した場合の処理にあります。子プロセスがエラーを発生させると、そのエラーメッセージはerrbufというバイトスライスに書き込まれます。このerrbufは、エラーメッセージを格納するために事前に確保された固定サイズのバッファです。

元のコードでは、エラーメッセージがerrbufに書き込まれた後、その内容をGoの文字列に変換する際に、string(errbuf[:])という形式を使用していました。これはerrbufスライス全体の容量を文字列として解釈することを意味します。しかし、実際にエラーメッセージが書き込まれたのはerrbufの先頭からnバイト目までであり、nは実際に書き込まれたバイト数を示します。

したがって、errbufnバイト目以降には、以前のデータや初期化されていないメモリの内容が残っている可能性があり、これらがGoの文字列に変換されると、ユーザーには意味不明な文字として表示されていました。

このコミットでは、この問題を解決するために、string(errbuf[:])string(errbuf[:n])に変更しました。この変更により、errbufスライスの先頭からnバイト目までのみがGoの文字列に変換されるようになります。これにより、実際にエラーメッセージとして有効な部分だけが文字列として扱われ、余分なガベージ文字が取り除かれ、クリーンなエラーメッセージが表示されるようになります。

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

--- a/src/pkg/syscall/exec_plan9.go
+++ b/src/pkg/syscall/exec_plan9.go
@@ -486,7 +486,7 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)\
 
 	if err != nil || n != 0 {
 		if n != 0 {
-			err = NewError(string(errbuf[:]))
+			err = NewError(string(errbuf[:n]))
 		}
 
 		// Child failed; wait for it to exit, to make sure

コアとなるコードの解説

変更された行は以下の通りです。

-			err = NewError(string(errbuf[:]))
+			err = NewError(string(errbuf[:n]))
  • errbuf: これは、子プロセスから返されたエラーメッセージを格納するためのバイトスライス(バッファ)です。
  • n: これは、errbufに実際に書き込まれたバイト数、つまりエラーメッセージの実際の長さを表す変数です。
  • errbuf[:]: これは、errbufスライスの全体を指します。元のコードでは、このスライス全体がGoの文字列に変換されていました。
  • errbuf[:n]: これは、errbufスライスの先頭からnバイト目まで(nを含まない)を指す新しいスライスを作成します。この新しいスライスは、実際にエラーメッセージが格納されている有効な部分のみを含みます。
  • string(...): バイトスライスをGoの文字列に変換する組み込み関数です。
  • NewError(...): syscallパッケージ内で定義されている、エラー文字列から新しいError型を生成する関数です。

この変更により、NewError関数に渡される文字列は、errbufの有効なデータ部分のみから生成されるようになり、結果としてエラーメッセージの末尾に付加されていた不要な文字が取り除かれました。これは、バッファの実際のデータ長を正確に指定することで、Goの文字列変換が意図しないメモリ領域を読み込むことを防ぐ、シンプルかつ効果的な修正です。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (syscallパッケージ、文字列の扱いなど)
  • Plan 9に関する一般的な情報源 (Wikipediaなど)
  • C言語における文字列の扱いに関する一般的な情報源
  • GitHubのコミットページ (https://github.com/golang/go/commit/3e37720bcebefe87459a7dd8f41164c0e2cfa4bc)
  • Go Code Review (https://golang.org/cl/66270043)