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

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

このコミットは、Go言語のsyscallパッケージにおけるLinux上でのStartProcess関数におけるファイルディスクリプタのシャッフルに関するバグ修正を扱っています。具体的には、子プロセスを起動する際にファイルディスクリプタが誤って上書きされる可能性があった問題を解決し、そのためのテストケースを追加しています。

コミット

commit b493f0a868982711903c01f759a56c448d908b12
Author: Cosmos Nicolaou <cnicolaou@google.com>
Date:   Tue Apr 30 11:52:23 2013 -0700

    syscall: fix a bug in the shuffling of file descriptors in StartProcess on Linux.
    
    R=iant, iant, r, bradfitz
    CC=golang-dev
    https://golang.org/cl/8334044
---
 src/pkg/os/exec/exec_test.go  | 127 ++++++++++++++++++++++++++++++++++++++++++\n src/pkg/syscall/exec_linux.go |  10 +++-\n 2 files changed, 135 insertions(+), 2 deletions(-)

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

https://github.com/golang/go/commit/b493f0a868982711903c01f759a56c448d908b12

元コミット内容

syscall: fix a bug in the shuffling of file descriptors in StartProcess on Linux.

このコミットは、Linux上でのStartProcessにおけるファイルディスクリプタのシャッフルに関するバグを修正します。

変更の背景

Go言語のos/execパッケージは、外部コマンドを実行するための機能を提供します。その内部では、OS固有のシステムコールを扱うsyscallパッケージが利用されます。特に、StartProcess関数は新しいプロセスを起動し、その際に親プロセスのファイルディスクリプタ(FD)を子プロセスに引き継ぎます。この引き継ぎの過程で、子プロセス側のFD番号を整理(シャッフル)する処理が行われます。

このコミット以前のsyscall.StartProcessの実装には、Linux環境において、ファイルディスクリプタのシャッフル処理にバグが存在していました。具体的には、ProcAttr.Files(標準入力、標準出力、標準エラー出力、およびExtraFilesの連結)として渡されたファイルディスクリプタを子プロセス内の連続したFDにマッピングする際に、特定の条件下でFDが誤って上書きされてしまう可能性がありました。特に、標準エラー出力(stderr)のFDが、ExtraFilesとして渡された別のFDによって上書きされてしまうという問題が報告されていました。

このバグは、FDの値やProcAttr.Files内のFDのインデックスに依存するデータ依存のケースで発生するため、再現が困難でした。特に、FD 3がProcAttr.Filesのインデックス4以降に存在し、かつStderrPipe()によって取得される標準エラー出力パイプの書き込み側のFDがProcAttr.Filesのサイズと同じである場合に問題が発生しやすかったようです。このような状況は、テストケースの配置や実行環境によってFDの割り当てが変わるため、発見が遅れたと考えられます。

この修正は、このようなファイルディスクリプタの衝突と上書きを防ぎ、プロセスの起動が意図通りに行われるようにするために導入されました。

前提知識の解説

  • ファイルディスクリプタ (File Descriptor, FD): Unix系OSにおいて、ファイルやソケット、パイプなどのI/Oリソースを識別するためにカーネルがプロセスに割り当てる非負の整数値です。標準入力は0、標準出力は1、標準エラー出力は2というFDが慣習的に割り当てられています。
  • os/execパッケージ: Go言語で外部コマンドを実行するためのパッケージです。Command構造体を通じてコマンドのパス、引数、環境変数、標準入出力などを設定し、Start()Run()メソッドで実行します。
  • syscallパッケージ: OS固有のシステムコールへの低レベルなインターフェースを提供するパッケージです。os/execのような高レベルなパッケージの内部で利用され、プロセスの生成、ファイルディスクリプタの操作など、OSカーネルと直接やり取りする機能を提供します。
  • StartProcessシステムコール: 新しいプロセスを生成し、指定された実行可能ファイルをそのプロセス空間で実行するためのシステムコールです。この際、親プロセスのファイルディスクリプタの一部を子プロセスに引き継ぐことができます。
  • ProcAttr.Files: syscall.ProcAttr構造体の一部で、子プロセスに引き継ぐファイルディスクリプタのリストを定義します。このリストの最初の3つの要素は通常、標準入力、標準出力、標準エラー出力に対応し、それ以降はExtraFilesとして追加のFDが指定されます。
  • dup2システムコール: 既存のファイルディスクリプタを、指定された新しいファイルディスクリプタ番号に複製するシステムコールです。これにより、あるFDが指すリソースを別のFD番号で参照できるようになります。プロセスの起動時に、親プロセスのFDを子プロセスの特定のFD番号にマッピングする際によく使用されます。
  • nextfd: 新しいファイルディスクリプタを割り当てる際に、次に利用可能なFD番号の候補を示す内部変数です。この値が適切に管理されていないと、既存のFDを上書きしてしまう可能性があります。

技術的詳細

このバグは、syscall/exec_linux.go内のforkAndExecInChild関数、特にファイルディスクリプタをシャッフルするロジックに起因していました。StartProcessは、ProcAttr.Filesに指定されたFDを、子プロセス内で0, 1, 2, ... といった連続したFD番号にマッピングし直します。このマッピングはdup2システムコールを使って行われます。

問題は、dup2を実行する際に、一時的に使用するFD番号(nextfd)が、子プロセスに引き継がれるべき既存のFDと衝突する可能性があったことです。特に、fd[i] < iとなるようなFDが存在する場合、つまり、親プロセスのFD番号が子プロセスで割り当てられるべきFD番号よりも小さい場合に、dup2が既存のFDを上書きしてしまうリスクがありました。

修正前は、nextfdが単にlen(attr.Files)(引き継ぐFDの総数)から始まるように設定されていました。しかし、もしattr.Filesに含まれるFDの中にlen(attr.Files)よりも大きな値が存在し、かつそのFDが子プロセスで重要な役割(例えば標準エラー出力)を担っている場合、dup2がそのFDを上書きしてしまう可能性がありました。

このコミットでは、nextfdの計算方法が変更されました。

  1. まず、attr.Filesに含まれるすべてのFDを走査し、その中で最も大きなFD番号を見つけます。
  2. nextfdを、その最大FD番号に1を加えた値に設定します。

これにより、nextfdは子プロセスに引き継がれるどのFD番号とも衝突しない、十分に大きな値が保証されます。このnextfdを使って一時的なdup2操作を行うことで、既存の重要なFDが誤って上書きされることを防ぎます。

また、このバグを再現し、修正を検証するためにTestExtraFilesFDShuffleという新しいテストケースがsrc/pkg/os/exec/exec_test.goに追加されました。このテストは、特定のFD番号(特にFD 3)とパイプの組み合わせを意図的に作成し、stderrExtraFilesによって上書きされないことを確認します。テストの複雑さは、バグが特定のFD割り当てに依存していたことを示しています。

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

src/pkg/os/exec/exec_test.go

  • TestExtraFilesFDShuffleという新しいテスト関数が追加されました。このテストは、syscall.StartProcessがファイルディスクリプタを正しくシャッフルし、特にstderrExtraFilesによって上書きされないことを検証します。
  • テスト内で、os.Pipe()を使って特定のFD番号(例: FD 3)を持つパイプを作成し、Cmd.ExtraFilesに含めることで、バグが再現しやすい条件をシミュレートしています。
  • 子プロセス側では、helperCommand"extraFilesAndPipes"ケースが追加され、親から渡されたパイプからデータを読み取り、標準エラー出力に書き出すことで、FDのシャッフルが正しく行われたかを確認します。
  • TestExtraFilesRace関数に、ExtraFilesを閉じるためのdeferステートメントが追加されました。

src/pkg/syscall/exec_linux.go

  • forkAndExecInChild関数内のファイルディスクリプタのシャッフルロジックが変更されました。

  • 以前はnextfd = int(len(fd))と単純に設定されていたnextfdの初期値が、attr.Files内のすべてのFDをチェックし、その中で最大のFD番号よりも1大きい値に設定されるようになりました。

    // 修正前:
    // nextfd = int(len(fd))
    
    // 修正後:
    // Guard against side effects of shuffling fds below.
    // Make sure that nextfd is beyond any currently open files so
    // that we can't run the risk of overwriting any of them.
    fd := make([]int, len(attr.Files))
    nextfd = len(attr.Files) // まずは引き継ぐFDの数で初期化
    for i, ufd := range attr.Files {
        if nextfd < int(ufd) { // 既存のFDの中で最大のものを探す
            nextfd = int(ufd)
        }
        fd[i] = int(ufd)
    }
    nextfd++ // 最大のFDよりも1大きい値に設定
    

コアとなるコードの解説

src/pkg/syscall/exec_linux.go の変更点

この変更の核心は、forkAndExecInChild関数におけるnextfdの計算ロジックです。

	// Guard against side effects of shuffling fds below.
	// Make sure that nextfd is beyond any currently open files so
	// that we can't run the risk of overwriting any of them.
	fd := make([]int, len(attr.Files))
	nextfd = len(attr.Files) // (1) まず、引き継ぐFDの総数でnextfdを初期化
	for i, ufd := range attr.Files {
		if nextfd < int(ufd) { // (2) attr.Files内の各FDをチェックし、nextfdよりも大きいFDがあればnextfdを更新
			nextfd = int(ufd)
		}
		fd[i] = int(ufd) // (3) fdスライスにFDをコピー
	}
	nextfd++ // (4) 最終的に、nextfdをattr.Files内の最大のFD番号よりも1大きい値に設定
  1. nextfd = len(attr.Files): nextfdは、dup2システムコールで一時的に使用されるファイルディスクリプタ番号の候補です。まず、子プロセスに引き継がれるファイルディスクリプタの総数(len(attr.Files))で初期化されます。これは、通常、子プロセスで0, 1, 2...と割り当てられるFDの範囲外の番号を確保しようとする意図があります。
  2. for i, ufd := range attr.Files { if nextfd < int(ufd) { nextfd = int(ufd) } ... }: ここが重要な変更点です。attr.Filesには、親プロセスで開かれている実際のファイルディスクリプタ番号が含まれています。このループでは、attr.Files内のすべてのFD番号を走査し、もし現在のnextfdの値よりも大きなFD番号が見つかった場合、nextfdをその大きなFD番号に更新します。これにより、nextfdattr.Filesに含まれるすべてのFD番号よりも少なくとも同じか大きい値になります。
  3. fd[i] = int(ufd): これは、attr.Filesの値をfdという内部スライスにコピーしているだけです。
  4. nextfd++: ループが終了した後、nextfdattr.Files内の最大のFD番号と同じかそれ以上の値になっています。最後にnextfdをインクリメントすることで、nextfdattr.Files内のどのFD番号とも衝突しない、確実に利用可能なFD番号となります。このnextfdが、dup2システムコールで一時的なFDとして使用されることで、既存の重要なFDが上書きされるリスクがなくなります。

この修正により、StartProcessが子プロセスを起動する際に、ファイルディスクリプタのシャッフル処理がより堅牢になり、予期せぬFDの上書きバグが解消されました。

src/pkg/os/exec/exec_test.goTestExtraFilesFDShuffle

このテストは、バグの再現と修正の検証のために非常に巧妙に設計されています。

  • 特定のFD割り当ての強制:
    • rd, wr, _ := os.Pipe(): パイプを作成し、rd.Fd()3であることを確認しています。これは、バグがFD 3と関連していたため、テスト環境でこの特定のFD番号を確保しようとしています。
    • c.ExtraFiles = append(c.ExtraFiles, rd): このFD 3をExtraFilesの最後に配置しています。
    • stderrFd := int(stderr.(*os.File).Fd()): 標準エラー出力のFDを取得し、それが((len(c.ExtraFiles) + 3) - 1)と一致するかをチェックしています。これは、バグが特定のFD割り当ての組み合わせで発生したため、その条件を再現しようとしています。
  • データフローの検証:
    • 親プロセスで複数のパイプを作成し、それぞれに異なるデータ("a", "b")を書き込み、ExtraFilesとして子プロセスに渡します。
    • 子プロセス("extraFilesAndPipes"ヘルパー)は、渡されたFDからデータを読み取り、それらを連結して標準エラー出力に書き出します。
    • 親プロセスは子プロセスの標準エラー出力を読み取り、期待される文字列("child: ab_LAST")と一致するかを検証します。
  • タイムアウト: time.After(5 * time.Second)を使って、子プロセスからの読み取りがタイムアウトしないことを確認し、デッドロックやハングアップがないことを保証しています。

このテストは、単に機能が動作するかどうかだけでなく、特定の条件下でのファイルディスクリプタの正確なマッピングと、それによるデータ破損がないことを厳密に検証しています。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (GitHub): https://github.com/golang/go
  • Goのコードレビューシステム (Gerrit): https://go-review.googlesource.com/ (コミットメッセージに記載されているhttps://golang.org/cl/8334044は、Gerritの変更リストへのリンクです。)
  • ファイルディスクリプタ、dup2システムコールに関する一般的なUnix/Linuxプログラミングの資料。
  • Go言語のプロセスの起動に関する内部実装の解説記事(もしあれば)。
  • GoのIssueトラッカー(もしこのバグに関するIssueが公開されていれば)。

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

このコミットは、Go言語のsyscallパッケージにおけるLinux上でのStartProcess関数におけるファイルディスクリプタのシャッフルに関するバグ修正を扱っています。具体的には、子プロセスを起動する際にファイルディスクリプタが誤って上書きされる可能性があった問題を解決し、そのためのテストケースを追加しています。

コミット

commit b493f0a868982711903c01f759a56c448d908b12
Author: Cosmos Nicolaou <cnicolaou@google.com>
Date:   Tue Apr 30 11:52:23 2013 -0700

    syscall: fix a bug in the shuffling of file descriptors in StartProcess on Linux.
    
    R=iant, iant, r, bradfitz
    CC=golang-dev
    https://golang.org/cl/8334044
---
 src/pkg/os/exec/exec_test.go  | 127 ++++++++++++++++++++++++++++++++++++++++++\n src/pkg/syscall/exec_linux.go |  10 +++-\n 2 files changed, 135 insertions(+), 2 deletions(-)

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

https://github.com/golang/go/commit/b493f0a868982711903c01f759a56c448d908b12

元コミット内容

syscall: fix a bug in the shuffling of file descriptors in StartProcess on Linux.

このコミットは、Linux上でのStartProcessにおけるファイルディスクリプタのシャッフルに関するバグを修正します。

変更の背景

Go言語のos/execパッケージは、外部コマンドを実行するための機能を提供します。その内部では、OS固有のシステムコールを扱うsyscallパッケージが利用されます。特に、StartProcess関数は新しいプロセスを起動し、その際に親プロセスのファイルディスクリプタ(FD)を子プロセスに引き継ぎます。この引き継ぎの過程で、子プロセス側のFD番号を整理(シャッフル)する処理が行われます。

このコミット以前のsyscall.StartProcessの実装には、Linux環境において、ファイルディスクリプタのシャッフル処理にバグが存在していました。具体的には、ProcAttr.Files(標準入力、標準出力、標準エラー出力、およびExtraFilesの連結)として渡されたファイルディスクリプタを子プロセス内の連続したFDにマッピングする際に、特定の条件下でFDが誤って上書きされてしまう可能性がありました。特に、標準エラー出力(stderr)のFDが、ExtraFilesとして渡された別のFDによって上書きされてしまうという問題が報告されていました。

このバグは、FDの値やProcAttr.Files内のFDのインデックスに依存するデータ依存のケースで発生するため、再現が困難でした。特に、FD 3がProcAttr.Filesのインデックス4以降に存在し、かつStderrPipe()によって取得される標準エラー出力パイプの書き込み側のFDがProcAttr.Filesのサイズと同じである場合に問題が発生しやすかったようです。このような状況は、テストケースの配置や実行環境によってFDの割り当てが変わるため、発見が遅れたと考えられます。

この修正は、このようなファイルディスクリプタの衝突と上書きを防ぎ、プロセスの起動が意図通りに行われるようにするために導入されました。

前提知識の解説

  • ファイルディスクリプタ (File Descriptor, FD): Unix系OSにおいて、ファイルやソケット、パイプなどのI/Oリソースを識別するためにカーネルがプロセスに割り当てる非負の整数値です。標準入力は0、標準出力は1、標準エラー出力は2というFDが慣習的に割り当てられています。
  • os/execパッケージ: Go言語で外部コマンドを実行するためのパッケージです。Command構造体を通じてコマンドのパス、引数、環境変数、標準入出力などを設定し、Start()Run()メソッドで実行します。
  • syscallパッケージ: OS固有のシステムコールへの低レベルなインターフェースを提供するパッケージです。os/execのような高レベルなパッケージの内部で利用され、プロセスの生成、ファイルディスクリプタの操作など、OSカーネルと直接やり取りする機能を提供します。
  • StartProcessシステムコール: 新しいプロセスを生成し、指定された実行可能ファイルをそのプロセス空間で実行するためのシステムコールです。この際、親プロセスのファイルディスクリプタの一部を子プロセスに引き継ぐことができます。
  • ProcAttr.Files: syscall.ProcAttr構造体の一部で、子プロセスに引き継ぐファイルディスクリプタのリストを定義します。このリストの最初の3つの要素は通常、標準入力、標準出力、標準エラー出力に対応し、それ以降はExtraFilesとして追加のFDが指定されます。
  • dup2システムコール: 既存のファイルディスクリプタを、指定された新しいファイルディスクリプタ番号に複製するシステムコールです。これにより、あるFDが指すリソースを別のFD番号で参照できるようになります。プロセスの起動時に、親プロセスのFDを子プロセスの特定のFD番号にマッピングする際によく使用されます。
  • nextfd: 新しいファイルディスクリプタを割り当てる際に、次に利用可能なFD番号の候補を示す内部変数です。この値が適切に管理されていないと、既存のFDを上書きしてしまう可能性があります。

技術的詳細

このバグは、syscall/exec_linux.go内のforkAndExecInChild関数、特にファイルディスクリプタをシャッフルするロジックに起因していました。StartProcessは、ProcAttr.Filesに指定されたFDを、子プロセス内で0, 1, 2, ... といった連続したFD番号にマッピングし直します。このマッピングはdup2システムコールを使って行われます。

問題は、dup2を実行する際に、一時的に使用するFD番号(nextfd)が、子プロセスに引き継がれるべき既存のFDと衝突する可能性があったことです。特に、fd[i] < iとなるようなFDが存在する場合、つまり、親プロセスのFD番号が子プロセスで割り当てられるべきFD番号よりも小さい場合に、dup2が既存のFDを上書きしてしまうリスクがありました。

修正前は、nextfdが単にlen(attr.Files)(引き継ぐFDの総数)から始まるように設定されていました。しかし、もしattr.Filesに含まれるFDの中にlen(attr.Files)よりも大きな値が存在し、かつそのFDが子プロセスで重要な役割(例えば標準エラー出力)を担っている場合、dup2がそのFDを上書きしてしまう可能性がありました。

このコミットでは、nextfdの計算方法が変更されました。

  1. まず、attr.Filesに含まれるすべてのFDを走査し、その中で最も大きなFD番号を見つけます。
  2. nextfdを、その最大FD番号に1を加えた値に設定します。

これにより、nextfdは子プロセスに引き継がれるどのFD番号とも衝突しない、十分に大きな値が保証されます。このnextfdを使って一時的なdup2操作を行うことで、既存の重要なFDが誤って上書きされることを防ぎます。

また、このバグを再現し、修正を検証するためにTestExtraFilesFDShuffleという新しいテストケースがsrc/pkg/os/exec/exec_test.goに追加されました。このテストは、特定のFD番号(特にFD 3)とパイプの組み合わせを意図的に作成し、stderrExtraFilesによって上書きされないことを確認します。テストの複雑さは、バグが特定のFD割り当てに依存していたことを示しています。

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

src/pkg/os/exec/exec_test.go

  • TestExtraFilesFDShuffleという新しいテスト関数が追加されました。このテストは、syscall.StartProcessがファイルディスクリプタを正しくシャッフルし、特にstderrExtraFilesによって上書きされないことを検証します。
  • テスト内で、os.Pipe()を使って特定のFD番号(例: FD 3)を持つパイプを作成し、Cmd.ExtraFilesに含めることで、バグが再現しやすい条件をシミュレートしています。
  • 子プロセス側では、helperCommand"extraFilesAndPipes"ケースが追加され、親から渡されたパイプからデータを読み取り、標準エラー出力に書き出すことで、FDのシャッフルが正しく行われたかを確認します。
  • TestExtraFilesRace関数に、ExtraFilesを閉じるためのdeferステートメントが追加されました。

src/pkg/syscall/exec_linux.go

  • forkAndExecInChild関数内のファイルディスクリプタのシャッフルロジックが変更されました。

  • 以前はnextfd = int(len(fd))と単純に設定されていたnextfdの初期値が、attr.Files内のすべてのFDをチェックし、その中で最大のFD番号よりも1大きい値に設定されるようになりました。

    // 修正前:
    // nextfd = int(len(fd))
    
    // 修正後:
    // Guard against side effects of shuffling fds below.
    // Make sure that nextfd is beyond any currently open files so
    // that we can't run the risk of overwriting any of them.
    fd := make([]int, len(attr.Files))
    nextfd = len(attr.Files) // まずは引き継ぐFDの数で初期化
    for i, ufd := range attr.Files {
        if nextfd < int(ufd) { // 既存のFDの中で最大のものを探す
            nextfd = int(ufd)
        }
        fd[i] = int(ufd)
    }
    nextfd++ // 最大のFDよりも1大きい値に設定
    

コアとなるコードの解説

src/pkg/syscall/exec_linux.go の変更点

この変更の核心は、forkAndExecInChild関数におけるnextfdの計算ロジックです。

	// Guard against side effects of shuffling fds below.
	// Make sure that nextfd is beyond any currently open files so
	// that we can't run the risk of overwriting any of them.
	fd := make([]int, len(attr.Files))
	nextfd = len(attr.Files) // (1) まず、引き継ぐFDの総数でnextfdを初期化
	for i, ufd := range attr.Files {
		if nextfd < int(ufd) { // (2) attr.Files内の各FDをチェックし、nextfdよりも大きいFDがあればnextfdを更新
			nextfd = int(ufd)
		}
		fd[i] = int(ufd) // (3) fdスライスにFDをコピー
	}
	nextfd++ // (4) 最終的に、nextfdをattr.Files内の最大のFD番号よりも1大きい値に設定
  1. nextfd = len(attr.Files): nextfdは、dup2システムコールで一時的に使用されるファイルディスクリプタ番号の候補です。まず、子プロセスに引き継がれるファイルディスクリプタの総数(len(attr.Files))で初期化されます。これは、通常、子プロセスで0, 1, 2...と割り当てられるFDの範囲外の番号を確保しようとする意図があります。
  2. for i, ufd := range attr.Files { if nextfd < int(ufd) { nextfd = int(ufd) } ... }: ここが重要な変更点です。attr.Filesには、親プロセスで開かれている実際のファイルディスクリプタ番号が含まれています。このループでは、attr.Files内のすべてのFD番号を走査し、もし現在のnextfdの値よりも大きなFD番号が見つかった場合、nextfdをその大きなFD番号に更新します。これにより、nextfdattr.Filesに含まれるすべてのFD番号よりも少なくとも同じか大きい値になります。
  3. fd[i] = int(ufd): これは、attr.Filesの値をfdという内部スライスにコピーしているだけです。
  4. nextfd++: ループが終了した後、nextfdattr.Files内の最大のFD番号と同じかそれ以上の値になっています。最後にnextfdをインクリメントすることで、nextfdattr.Files内のどのFD番号とも衝突しない、確実に利用可能なFD番号となります。このnextfdが、dup2システムコールで一時的なFDとして使用されることで、既存の重要なFDが上書きされるリスクがなくなります。

この修正により、StartProcessが子プロセスを起動する際に、ファイルディスクリプタのシャッフル処理がより堅牢になり、予期せぬFDの上書きバグが解消されました。

src/pkg/os/exec/exec_test.goTestExtraFilesFDShuffle

このテストは、バグの再現と修正の検証のために非常に巧妙に設計されています。

  • 特定のFD割り当ての強制:
    • rd, wr, _ := os.Pipe(): パイプを作成し、rd.Fd()3であることを確認しています。これは、バグがFD 3と関連していたため、テスト環境でこの特定のFD番号を確保しようとしています。
    • c.ExtraFiles = append(c.ExtraFiles, rd): このFD 3をExtraFilesの最後に配置しています。
    • stderrFd := int(stderr.(*os.File).Fd()): 標準エラー出力のFDを取得し、それが((len(c.ExtraFiles) + 3) - 1)と一致するかをチェックしています。これは、バグが特定のFD割り当ての組み合わせで発生したため、その条件を再現しようとしています。
  • データフローの検証:
    • 親プロセスで複数のパイプを作成し、それぞれに異なるデータ("a", "b")を書き込み、ExtraFilesとして子プロセスに渡します。
    • 子プロセス("extraFilesAndPipes"ヘルパー)は、渡されたFDからデータを読み取り、それらを連結して標準エラー出力に書き出します。
    • 親プロセスは子プロセスの標準エラー出力を読み取り、期待される文字列("child: ab_LAST")と一致するかを検証します。
  • タイムアウト: time.After(5 * time.Second)を使って、子プロセスからの読み取りがタイムアウトしないことを確認し、デッドロックやハングアップがないことを保証しています。

このテストは、単に機能が動作するかどうかだけでなく、特定の条件下でのファイルディスクリプタの正確なマッピングと、それによるデータ破損がないことを厳密に検証しています。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (GitHub): https://github.com/golang/go
  • Goのコードレビューシステム (Gerrit): https://go-review.googlesource.com/ (コミットメッセージに記載されているhttps://golang.org/cl/8334044は、Gerritの変更リストへのリンクです。)
  • ファイルディスクリプタ、dup2システムコールに関する一般的なUnix/Linuxプログラミングの資料。
  • Go言語のプロセスの起動に関する内部実装の解説記事(もしあれば)。
  • GoのIssueトラッカー(もしこのバグに関するIssueが公開されていれば)。