[インデックス 13381] ファイルの概要
このコミットは、Go言語のos
パッケージにおけるStartProcess
関数の挙動を修正し、POSIXシステム上でのchroot
(チェンジルート)環境下でのプロセス起動が再び正しく機能するようにしたものです。具体的には、SysProcAttr
が指定されている場合に、StartProcess
が実行するディレクトリ存在チェックをスキップすることで、chroot
環境との競合を解消しています。
コミット
commit d36c095da920cffef16da7f37ce725f9a07f6ae0
Author: Han-Wen Nienhuys <hanwen@google.com>
Date: Sun Jun 24 19:34:06 2012 -0400
os: make POSIX StartProcess work with chroot again.
Skip directory check in startProcess in the presence of
SysProcAttr.
Fixes #3649.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6297083
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d36c095da920cffef16da7f37ce725f9a07f6ae0
元コミット内容
このコミットは、Go言語の標準ライブラリであるos
パッケージ内のexec_posix.go
ファイルに対して行われた変更です。主な目的は、os.StartProcess
関数がchroot
環境下で正しく動作するように修正することです。以前のバージョンでは、SysProcAttr
が指定されている場合でも、StartProcess
がattr.Dir
(変更先のディレクトリ)の存在チェックを行っていました。このチェックがchroot
環境と競合し、プロセス起動が失敗する原因となっていました。今回の変更では、SysProcAttr
が存在する場合にこのディレクトリチェックをスキップすることで、問題を解決しています。
変更の背景
この変更は、Go言語のIssue #3649を修正するために行われました。Issue #3649は、os.StartProcess
関数がchroot
環境下で期待通りに動作しないというバグ報告です。
chroot
は、プロセスのルートディレクトリを変更するシステムコールであり、セキュリティの強化や隔離された実行環境の構築によく利用されます。例えば、コンテナ技術の基盤としても利用されることがあります。Go言語のos.StartProcess
関数は、新しいプロセスを起動するための汎用的なインターフェースを提供しており、os.ProcAttr
構造体を通じてプロセスの属性(作業ディレクトリ、環境変数、ファイルディスクリプタなど)を設定できます。さらに、os.ProcAttr.Sys
フィールドを通じて、syscall.SysProcAttr
構造体を指定することで、chroot
のようなOS固有のプロセス作成属性を適用することが可能です。
しかし、以前の実装では、StartProcess
がchroot
によって変更される可能性のあるディレクトリに対して、通常のファイルシステム上の存在チェックを行っていました。chroot
が適用されると、プロセスのファイルシステムビューは指定された新しいルートディレクトリに限定されます。このため、StartProcess
がchroot
後のパスを考慮せずに、chroot
前のファイルシステム上でディレクトリの存在を確認しようとすると、「そのようなファイルまたはディレクトリはありません」といったエラーが発生し、プロセス起動が失敗していました。
このコミットは、この誤ったディレクトリチェックを回避することで、chroot
が適用されるケースでもStartProcess
が正しく機能するように修正することを目的としています。
前提知識の解説
1. chroot
(チェンジルート)
chroot
は、Unix系OSに存在するシステムコールであり、特定のプロセスとその子プロセスに対して、ファイルシステムのルートディレクトリ(/
)を別のディレクトリに変更する機能です。これにより、プロセスは指定されたディレクトリツリーの外にあるファイルにアクセスできなくなります。
主な用途:
- セキュリティの強化(chroot jail): 悪意のあるプロセスがシステム全体にアクセスするのを防ぐために、隔離された環境(「chroot監獄」)を作成します。
- 開発・テスト環境の隔離: 開発中のソフトウェアがホストシステムに影響を与えないように、独立した環境でテストを行います。
- システム復旧: 破損したシステムを、別のブート可能な環境から
chroot
して修復します。 - パッケージビルド: クリーンなビルド環境を確保し、意図しない依存関係の混入を防ぎます。
注意点:
chroot
はセキュリティメカニズムとして利用されますが、完全な隔離を提供するものではありません。特にroot権限を持つプロセスはchroot
環境から抜け出すことが可能です。より強力な隔離には、コンテナ技術(Dockerなど)や仮想化技術が用いられます。
2. Go言語のos
パッケージとプロセス管理
Go言語のos
パッケージは、オペレーティングシステムとのインタラクションを提供します。プロセス管理においては、os.StartProcess
関数が中心的な役割を担います。
-
os.StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error)
:name
: 実行するプログラムのパス。argv
: プログラムに渡す引数のスライス。attr
:*os.ProcAttr
型の構造体で、新しいプロセスの属性(作業ディレクトリ、環境変数、標準入出力のリダイレクトなど)を設定します。
-
os.ProcAttr
構造体:Dir string
: プロセスの作業ディレクトリ。Env []string
: プロセスの環境変数。Files []*File
: プロセスに渡すファイルディスクリプタ。Sys *syscall.SysProcAttr
: OS固有のプロセス作成属性を指定するためのフィールド。このフィールドはsyscall
パッケージのSysProcAttr
構造体へのポインタを取ります。
3. syscall
パッケージとsyscall.SysProcAttr
Go言語のsyscall
パッケージは、低レベルのOSプリミティブへのアクセスを提供します。これには、システムコールを直接呼び出すための関数や、OS固有のデータ構造が含まれます。
syscall.SysProcAttr
構造体:- この構造体はOSに依存し、プロセス作成時にOS固有のオプションを設定するために使用されます。
- Unix系システムでは、
Chroot string
フィールドが含まれており、ここにchroot
するディレクトリのパスを指定することで、新しいプロセスがそのディレクトリをルートとして認識するように設定できます。
4. Issue #3649
このコミットが修正するGo言語のIssue #3649は、os.StartProcess
がSysProcAttr
を通じてchroot
が設定されているにもかかわらず、attr.Dir
で指定されたディレクトリの存在チェックを、chroot
が適用される前のファイルシステムパスで行ってしまうという問題でした。これにより、chroot
環境下でプロセスを起動しようとすると、存在しないディレクトリへのアクセスとしてエラーが発生していました。
技術的詳細
os.StartProcess
関数は、新しいプロセスを起動する前に、いくつかの前処理を行います。その一つに、os.ProcAttr
で指定された作業ディレクトリ(attr.Dir
)が存在するかどうかのチェックがあります。このチェックは、Stat(attr.Dir)
を呼び出すことで行われます。
問題は、chroot
が適用される場合、プロセスのファイルシステムビューが変更されることです。StartProcess
が呼び出される時点では、まだchroot
システムコールが実行されていないため、Stat(attr.Dir)
はホストのファイルシステム上でattr.Dir
の存在を確認しようとします。しかし、もしattr.Dir
がchroot
後の新しいルートディレクトリ内のパスを指している場合、ホストのファイルシステム上ではそのパスは存在しないか、意図しない場所を指している可能性があります。
このコミットの変更前は、if attr != nil && attr.Dir != ""
という条件でディレクトリチェックが実行されていました。この条件は、SysProcAttr
が設定されているかどうかに関わらず、attr.Dir
が空でなければ常にチェックを実行していました。
今回の修正では、この条件に&& attr.Sys == nil
という条件が追加されました。これにより、attr.Sys
(つまりsyscall.SysProcAttr
)がnil
でない場合、すなわちchroot
などのOS固有のプロセス属性が設定されている場合には、ディレクトリの存在チェックをスキップするようになります。
なぜこの変更が問題を解決するのか?
SysProcAttr
が設定されているということは、プロセスが特別な環境(この場合はchroot
)で起動される可能性が高いことを示唆しています。このような場合、attr.Dir
はchroot
後の新しいルートディレクトリを基準としたパスである可能性が高く、chroot
が適用される前の段階でそのパスの存在をホストのファイルシステム上でチェックすることは無意味であり、むしろ誤ったエラーを引き起こす原因となります。
SysProcAttr
が存在する場合にディレクトリチェックをスキップすることで、StartProcess
はchroot
が適用される前に誤ったエラーを発生させることなく、プロセス起動の次のステップに進むことができます。chroot
システムコールは、プロセスが実際に起動される際にOSによって適用されるため、このチェックをスキップしてもセキュリティ上の問題は発生しません。むしろ、chroot
の意図した動作を妨げないようにするための適切な修正と言えます。
コアとなるコードの変更箇所
変更はsrc/pkg/os/exec_posix.go
ファイル内のstartProcess
関数にあります。
--- a/src/pkg/os/exec_posix.go
+++ b/src/pkg/os/exec_posix.go
@@ -19,9 +19,10 @@ var (
)
func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
- // Double-check existence of the directory we want
+ // If there is no SysProcAttr (ie. no Chroot or changed
+ // UID/GID), double-check existence of the directory we want
// to chdir into. We can make the error clearer this way.
- if attr != nil && attr.Dir != "" {
+ if attr != nil && attr.Sys == nil && attr.Dir != "" {
if _, err := Stat(attr.Dir); err != nil {
pe := err.(*PathError)
pe.Op = "chdir"
コアとなるコードの解説
変更された行は以下の通りです。
- if attr != nil && attr.Dir != "" {
+ if attr != nil && attr.Sys == nil && attr.Dir != "" {
-
変更前:
if attr != nil && attr.Dir != ""
- これは、「
attr
がnil
ではなく、かつattr.Dir
が空文字列でない場合」に、attr.Dir
の存在チェックを実行するという条件でした。 - この条件は、
SysProcAttr
が設定されているかどうかに関わらず、常にディレクトリチェックを行っていました。
- これは、「
-
変更後:
if attr != nil && attr.Sys == nil && attr.Dir != ""
- 新しく追加された条件は
&& attr.Sys == nil
です。 - これにより、条件は「
attr
がnil
ではなく、かつattr.Sys
がnil
であり、かつattr.Dir
が空文字列でない場合」にディレクトリチェックを実行するように変更されました。 - つまり、
attr.Sys
がnil
でない(syscall.SysProcAttr
が設定されている)場合は、このディレクトリチェックのブロック全体がスキップされることになります。
- 新しく追加された条件は
この修正により、chroot
やUID/GIDの変更など、OS固有のプロセス属性が設定されている場合には、StartProcess
がattr.Dir
の存在を事前にチェックしなくなり、chroot
環境下でのプロセス起動がスムーズに行われるようになりました。コメントも「SysProcAttr
がない場合(つまりChroot
やUID/GIDの変更がない場合)にのみ、変更先のディレクトリの存在を再確認する」という意図を明確にするように更新されています。
関連リンク
- Go言語の
os
パッケージドキュメント: https://pkg.go.dev/os - Go言語の
syscall
パッケージドキュメント: https://pkg.go.dev/syscall - Go言語のIssue #3649 (このコミットが修正したIssue): 検索結果からは直接的なGo言語のIssue #3649のリンクは見つかりませんでしたが、コミットメッセージに記載されているため、Goの公式Issueトラッカーで検索することで詳細を確認できる可能性があります。
参考にした情報源リンク
- Go言語の
os.StartProcess
に関するドキュメント: https://pkg.go.dev/os#StartProcess - Go言語の
syscall.SysProcAttr
に関するドキュメント: https://pkg.go.dev/syscall#SysProcAttr chroot
に関するWikipedia記事: https://ja.wikipedia.org/wiki/Chrootchroot
の概念と使用例に関する情報(Web検索結果より)