[インデックス 13583] ファイルの概要
このコミットは、Go言語のsyscall
パッケージ内のexec_plan9.go
ファイルに対する変更です。このファイルは、Plan 9オペレーティングシステム上でのプロセス実行(forkExec
関数)に関連するシステムコールを扱うためのGo言語のインターフェースを提供します。具体的には、新しいプロセスをフォークし、指定されたプログラムを実行する際の環境変数の処理に関するバグ修正が含まれています。
コミット
commit 8b5d4c3c0310c1669f8abd0b159985d80771e9f7
Author: Alexey Borzenkov <snaury@gmail.com>
Date: Mon Aug 6 16:24:08 2012 -0400
syscall: fix plan9 build broken by CL 6458050
R=golang-dev, rsc
CC=golang-dev, r, yarikos
https://golang.org/cl/6454104
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8b5d4c3c0310c1669f8abd0b159985d80771e9f7
元コミット内容
syscall: fix plan9 build broken by CL 6458050
変更の背景
このコミットは、以前の変更リスト(Change List, CL)であるCL 6458050
によって引き起こされたPlan 9ビルドの破損を修正するために導入されました。CL 6458050
は、Go言語の標準ライブラリ、特にsyscall
パッケージにおける文字列とバイトポインタの扱いに関する変更を含んでいた可能性があります。
具体的には、環境変数を処理する際に使用されていたBytePtrFromString
関数が、Plan 9環境において問題を引き起こしていました。BytePtrFromString
はGoの文字列をCスタイルのヌル終端バイト配列に変換し、そのポインタを返すことを意図していますが、Plan 9の特定のコンテキストやメモリ管理の挙動と合致せず、ビルドエラーや実行時エラーの原因となっていたと考えられます。
この修正は、Plan 9上でのGoプログラムの安定性と互換性を維持するために不可欠でした。
前提知識の解説
syscall
パッケージ: Go言語の標準ライブラリの一部で、オペレーティングシステムの低レベルな機能(システムコール)にアクセスするためのインターフェースを提供します。ファイル操作、プロセス管理、ネットワーク通信など、OSに依存する多くの機能がこのパッケージを通じて実現されます。- Plan 9: ベル研究所で開発された分散オペレーティングシステムです。Unixの概念をさらに推し進め、すべてのリソースをファイルとして表現するという哲学を持っています。Go言語はPlan 9の設計思想に影響を受けており、初期のGo開発環境ではPlan 9が重要な役割を果たしていました。そのため、Goの標準ライブラリにはPlan 9固有のシステムコールを扱うためのコードが含まれています。
- 環境変数: プロセスが実行される環境に関する情報を提供するキーと値のペアです。例えば、
PATH
環境変数は実行可能ファイルの検索パスを指定します。プログラムが外部プロセスを起動する際、これらの環境変数を新しいプロセスに引き継ぐことがよくあります。 BytePtrFromString
: Go言語の文字列(UTF-8エンコードされたバイト列)を、C言語の文字列(ヌル終端バイト配列)として扱うためのポインタに変換する関数です。システムコールの中には、C言語のAPIのようにヌル終端文字列を期待するものがあるため、このような変換が必要になります。しかし、GoのガベージコレクタとCのポインタの相互運用には注意が必要で、特にGoの文字列が一時的なメモリ領域に割り当てられている場合、そのポインタがすぐに無効になる可能性があります。exec
システムコール: 新しいプログラムを実行するために使用されるシステムコール群です。既存のプロセスを新しいプログラムイメージで上書きします。forkExec
のような関数は、通常、fork
(新しいプロセスを作成)とexec
(新しいプログラムを実行)の組み合わせを抽象化したものです。
技術的詳細
問題の核心は、syscall
パッケージがPlan 9上でプロセスを起動する際に、環境変数をどのように準備して渡すかという点にありました。
以前のコードでは、環境変数の値(v[i+1:]
)をBytePtrFromString
に渡し、その結果得られるバイトポインタをenvItem
構造体に格納していました。BytePtrFromString
はGoの文字列からバイトポインタを生成しますが、このポインタが指すメモリ領域はGoのガベージコレクタによって管理されており、Goの文字列がスコープを外れたり、ガベージコレクションが実行されたりすると、ポインタが指すデータが無効になる可能性があります。
Plan 9のexec
システムコールが環境変数を読み取る前に、BytePtrFromString
によって生成された一時的なバイト配列が解放されてしまう、あるいは移動されてしまうという競合状態やライフサイクル管理の問題が発生していたと考えられます。これにより、exec
システムコールが不正なメモリを読み取ろうとし、ビルドの失敗や実行時エラーにつながっていました。
修正では、この問題を回避するために、環境変数の値をBytePtrFromString
で直接ポインタに変換するのではなく、明示的に新しいバイトスライスを作成し、そこに環境変数の値をコピーするように変更されました。
envvalue := make([]byte, len(v)-i)
: 環境変数の値の長さに合わせて、新しいバイトスライスenvvalue
を確保します。これにより、このバイトスライスはGoのヒープ上に独立して存在し、ガベージコレクタによってすぐに回収されることはありません。copy(envvalue, v[i+1:])
: 環境変数の実際の値(v[i+1:]
)を、新しく確保したenvvalue
スライスにコピーします。&envvalue[0]
: 新しいバイトスライスの先頭要素へのポインタを取得し、これをenvItem
構造体に格納します。このポインタは、envvalue
スライスが有効である限り、有効なメモリ領域を指し続けます。
この変更により、exec
システムコールが環境変数を読み取る際に、データが常に有効なメモリ領域に存在することが保証され、Plan 9ビルドの破損が修正されました。これは、GoのガベージコレクタとCスタイルのポインタを扱う際の一般的な注意点を示す良い例です。
コアとなるコードの変更箇所
--- a/src/pkg/syscall/exec_plan9.go
+++ b/src/pkg/syscall/exec_plan9.go
@@ -419,11 +419,9 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)\
if err != nil {
return 0, err
}
- envvalue, err := BytePtrFromString(v[i+1:])
- if err != nil {
- return 0, err
- }
- envvParsed = append(envvParsed, envItem{envname, envvalue, len(v) - i})\
+ envvalue := make([]byte, len(v)-i)
+ copy(envvalue, v[i+1:])
+ envvParsed = append(envvParsed, envItem{envname, &envvalue[0], len(v) - i})\
}
}
コアとなるコードの解説
変更されたコードブロックは、forkExec
関数内で環境変数を解析し、準備する部分です。
-
変更前:
envvalue, err := BytePtrFromString(v[i+1:]) if err != nil { return 0, err } envvParsed = append(envvParsed, envItem{envname, envvalue, len(v) - i})
ここでは、環境変数の値(
v[i+1:]
)をBytePtrFromString
関数に渡していました。この関数は、Goの文字列からCスタイルのヌル終端バイト配列へのポインタを返します。しかし、このポインタが指すメモリはGoのガベージコレクタによって管理されており、forkExec
がシステムコールを呼び出す前に、そのメモリが移動または解放される可能性がありました。これがPlan 9ビルドの破損の原因でした。また、BytePtrFromString
がエラーを返す可能性も考慮されていました。 -
変更後:
envvalue := make([]byte, len(v)-i) copy(envvalue, v[i+1:]) envvParsed = append(envvParsed, envItem{envname, &envvalue[0], len(v) - i})
この修正では、
BytePtrFromString
の使用を完全に削除しました。envvalue := make([]byte, len(v)-i)
: まず、環境変数の値の長さに正確に一致する新しいバイトスライスenvvalue
を割り当てます。このスライスはGoのヒープ上に確保され、明示的に参照がなくなるまでガベージコレクションの対象になりません。copy(envvalue, v[i+1:])
: 次に、元の環境変数の値(v[i+1:]
)を、新しく作成したenvvalue
スライスにコピーします。これにより、環境変数のデータが安定したメモリ領域に格納されます。envvParsed = append(envvParsed, envItem{envname, &envvalue[0], len(v) - i})
: 最後に、envItem
構造体に、新しくコピーされたバイトスライスの先頭要素へのポインタ&envvalue[0]
を格納します。このポインタは、envvalue
スライスが有効である限り、安全に参照できます。
この変更により、環境変数のデータがシステムコールに渡されるまで確実にメモリ上に保持されるようになり、Plan 9上でのexec
システムコールの安定性が向上し、ビルドの破損が修正されました。
関連リンク
- 元の変更リスト(CL): https://golang.org/cl/6454104
参考にした情報源リンク
- CL 6458050に関するWeb検索結果: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF_fowp1gJ6fl91YdC5aoF6z3ashHAo9nMhJvkkz5beb_YnQqPvhiFCO8OjjlUFL5hXhG77GZ62cqL0SGh6YS3j_0P3EWUtpOxgbz6wcsiOnMF2S5AmPpgXpNyCoR8qqTlyi_uNkoF578Wm23SDOg==
- この検索結果は、
CL 6458050
がGo言語の変更であり、syscall
パッケージのPlan 9ビルドに影響を与えたバグを導入したことを示唆しています。
- この検索結果は、