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

[インデックス 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が指定されている場合でも、StartProcessattr.Dir(変更先のディレクトリ)の存在チェックを行っていました。このチェックがchroot環境と競合し、プロセス起動が失敗する原因となっていました。今回の変更では、SysProcAttrが存在する場合にこのディレクトリチェックをスキップすることで、問題を解決しています。

変更の背景

この変更は、Go言語のIssue #3649を修正するために行われました。Issue #3649は、os.StartProcess関数がchroot環境下で期待通りに動作しないというバグ報告です。

chrootは、プロセスのルートディレクトリを変更するシステムコールであり、セキュリティの強化や隔離された実行環境の構築によく利用されます。例えば、コンテナ技術の基盤としても利用されることがあります。Go言語のos.StartProcess関数は、新しいプロセスを起動するための汎用的なインターフェースを提供しており、os.ProcAttr構造体を通じてプロセスの属性(作業ディレクトリ、環境変数、ファイルディスクリプタなど)を設定できます。さらに、os.ProcAttr.Sysフィールドを通じて、syscall.SysProcAttr構造体を指定することで、chrootのようなOS固有のプロセス作成属性を適用することが可能です。

しかし、以前の実装では、StartProcesschrootによって変更される可能性のあるディレクトリに対して、通常のファイルシステム上の存在チェックを行っていました。chrootが適用されると、プロセスのファイルシステムビューは指定された新しいルートディレクトリに限定されます。このため、StartProcesschroot後のパスを考慮せずに、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.StartProcessSysProcAttrを通じてchrootが設定されているにもかかわらず、attr.Dirで指定されたディレクトリの存在チェックを、chrootが適用される前のファイルシステムパスで行ってしまうという問題でした。これにより、chroot環境下でプロセスを起動しようとすると、存在しないディレクトリへのアクセスとしてエラーが発生していました。

技術的詳細

os.StartProcess関数は、新しいプロセスを起動する前に、いくつかの前処理を行います。その一つに、os.ProcAttrで指定された作業ディレクトリ(attr.Dir)が存在するかどうかのチェックがあります。このチェックは、Stat(attr.Dir)を呼び出すことで行われます。

問題は、chrootが適用される場合、プロセスのファイルシステムビューが変更されることです。StartProcessが呼び出される時点では、まだchrootシステムコールが実行されていないため、Stat(attr.Dir)はホストのファイルシステム上でattr.Dirの存在を確認しようとします。しかし、もしattr.Dirchroot後の新しいルートディレクトリ内のパスを指している場合、ホストのファイルシステム上ではそのパスは存在しないか、意図しない場所を指している可能性があります。

このコミットの変更前は、if attr != nil && attr.Dir != ""という条件でディレクトリチェックが実行されていました。この条件は、SysProcAttrが設定されているかどうかに関わらず、attr.Dirが空でなければ常にチェックを実行していました。

今回の修正では、この条件に&& attr.Sys == nilという条件が追加されました。これにより、attr.Sys(つまりsyscall.SysProcAttr)がnilでない場合、すなわちchrootなどのOS固有のプロセス属性が設定されている場合には、ディレクトリの存在チェックをスキップするようになります。

なぜこの変更が問題を解決するのか?

SysProcAttrが設定されているということは、プロセスが特別な環境(この場合はchroot)で起動される可能性が高いことを示唆しています。このような場合、attr.Dirchroot後の新しいルートディレクトリを基準としたパスである可能性が高く、chrootが適用される前の段階でそのパスの存在をホストのファイルシステム上でチェックすることは無意味であり、むしろ誤ったエラーを引き起こす原因となります。

SysProcAttrが存在する場合にディレクトリチェックをスキップすることで、StartProcesschrootが適用される前に誤ったエラーを発生させることなく、プロセス起動の次のステップに進むことができます。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 != ""

    • これは、「attrnilではなく、かつattr.Dirが空文字列でない場合」に、attr.Dirの存在チェックを実行するという条件でした。
    • この条件は、SysProcAttrが設定されているかどうかに関わらず、常にディレクトリチェックを行っていました。
  • 変更後: if attr != nil && attr.Sys == nil && attr.Dir != ""

    • 新しく追加された条件は&& attr.Sys == nilです。
    • これにより、条件は「attrnilではなく、かつattr.Sysnilであり、かつattr.Dirが空文字列でない場合」にディレクトリチェックを実行するように変更されました。
    • つまり、attr.Sysnilでない(syscall.SysProcAttrが設定されている)場合は、このディレクトリチェックのブロック全体がスキップされることになります。

この修正により、chrootやUID/GIDの変更など、OS固有のプロセス属性が設定されている場合には、StartProcessattr.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トラッカーで検索することで詳細を確認できる可能性があります。

参考にした情報源リンク