[インデックス 13514] ファイルの概要
このコミットは、Go言語のCgoテストスイートにおけるsetgid
システムコール関連のテストの実行環境を、特定のオペレーティングシステム(GNU/Linux)に限定するように変更するものです。これにより、setgid
が特定の環境でハングアップする問題(Issue 3871)を回避し、テストの安定性を向上させています。
コミット
commit f7f91a0506df68f57906cfbe0e417a48c7a52495
Author: Ian Lance Taylor <iant@golang.org>
Date: Sat Jul 28 10:40:51 2012 -0700
misc/cgo/test: only run setgid test on GNU/Linux
Fixes #3874.
R=golang-dev, nj, r, minux.ma
CC=golang-dev
https://golang.org/cl/6446060
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f7f91a0506df68f57906cfbe0e417a48c7a52495
元コミット内容
misc/cgo/test: only run setgid test on GNU/Linux
このコミットは、setgid
システムコールに関するテストをGNU/Linux環境でのみ実行するように変更します。これは、Issue 3874を修正するためのものです。
変更の背景
この変更の背景には、Go言語のCgoテストスイートにおけるsetgid
システムコール関連のテストが、GNU/Linux以外のオペレーティングシステムでハングアップする問題(Issue 3871)がありました。setgid
はプロセスの実効グループIDを設定するためのシステムコールであり、その動作はOSによって異なる場合があります。特に、一部のOSでは特定の条件下でsetgid
がブロックされ、テストがタイムアウトしてしまう事象が発生していました。
Goのテストフレームワークは、異なるOS上でも一貫したテスト結果を得ることを目指していますが、OS固有の動作に起因する問題は、テストの信頼性を損なう可能性があります。このため、問題が確認されている環境でのみテストを実行し、それ以外の環境ではテストをスキップすることで、テストスイート全体の安定性を確保する必要がありました。
コミットメッセージに記載されているFixes #3874
は、この変更がGoのIssueトラッカー上の特定の課題を解決することを示しています。Issue 3874は、setgid
テストがGNU/Linux以外で失敗またはハングアップする問題に関連していると考えられます。
前提知識の解説
Cgo
Cgoは、GoプログラムからC言語のコードを呼び出すためのGoの機能です。これにより、Go言語で書かれたアプリケーションが、既存のCライブラリやOS固有のシステムコールを利用できるようになります。Cgoを使用すると、Goのコード内にCのコードを直接記述したり、Cのヘッダーファイルをインポートしたりすることができます。
Cgoの仕組みは、GoのコンパイラがCgoディレクティブを含むGoソースファイルを処理する際に、Cのコードを通常のCコンパイラ(通常はGCCやClang)に渡し、生成されたオブジェクトファイルをGoのオブジェクトファイルとリンクするというものです。これにより、GoとCの間の関数呼び出しが可能になります。
setgid
システムコール
setgid
はUnix系オペレーティングシステムで利用可能なシステムコールの一つで、呼び出し元のプロセスの実効グループID (effective group ID) を設定するために使用されます。実効グループIDは、プロセスがファイルやその他のシステムリソースにアクセスする際の権限を決定します。
通常、setgid(gid_t gid)
のように引数として新しいグループIDを渡します。このシステムコールは、セキュリティ上の理由から、特定の条件下でのみ成功します。例えば、非特権プロセスが実効グループIDを変更できるのは、そのプロセスが既に持っているグループIDのいずれかに変更する場合や、保存されたset-group-ID (saved set-group-ID) を実効グループIDに設定する場合などに限られます。特権プロセス(通常はroot権限を持つプロセス)は、任意のグループIDに実効グループIDを変更できます。
setgid
がハングアップする問題は、特にマルチスレッド環境や特定のOSのカーネル実装において、デッドロックやリソースの競合が発生することが原因で起こり得ます。これは、システムコールが内部的にロックを取得し、そのロックが解放されないまま他のスレッドが同じロックを待機し続ける場合に発生します。
テストの分離とOS固有のテスト
ソフトウェア開発において、テストはコードの品質と信頼性を保証するために不可欠です。しかし、OS固有の機能や動作に依存するコードの場合、すべてのOSで同じテストを実行することが適切でない場合があります。
このような場合、テストをOSごとに分離し、特定のOSでのみ実行するように設定することが一般的です。Goのテストフレームワークでは、ファイル名に_linux.go
や_windows.go
といったサフィックスを付けることで、そのファイルが特定のOSでのみコンパイル・実行されるように制御できます。これにより、OS固有のテストコードを整理し、テストスイート全体の実行効率と安定性を向上させることができます。
技術的詳細
このコミットの技術的詳細は、GoのビルドシステムとテストフレームワークのOS固有のファイル処理能力を活用して、setgid
テストをGNU/Linuxに限定している点にあります。
-
misc/cgo/test/basic.go
からのtestSetgid
関数の削除: 元のbasic.go
ファイルは、GoのCgoテストスイートの基本的なテストケースをまとめたものでした。このファイルからtestSetgid
関数が削除されたことで、このテストが汎用的なテストスイートの一部として実行されなくなりました。 -
misc/cgo/test/setgid_linux.go
の新規作成:setgid_linux.go
という新しいファイルが作成され、削除されたtestSetgid
関数がこのファイルに移動されました。Goのビルドシステムは、ファイル名に_linux.go
というサフィックスが付いている場合、そのファイルをLinux環境でのみコンパイルします。これにより、testSetgid
はLinux環境でのみ実行されるようになります。このファイルには、
setgid(0)
を呼び出すゴルーチンを起動し、その完了をチャネルで待機するロジックが含まれています。タイムアウト(5秒)を設定することで、setgid
がハングアップした場合にテストが無限に待機するのを防ぎ、エラーとして検出できるようにしています。 -
misc/cgo/test/cgo_linux_test.go
の新規作成: このファイルは、setgid_linux.go
で定義されたtestSetgid
関数を呼び出すためのテストエントリポイントを提供します。func TestSetgid(t *testing.T) { testSetgid(t) }
という形式で、Goのテストフレームワークが認識するテスト関数を定義しています。このファイルも_linux_test.go
というサフィックスを持つため、Linux環境でのみコンパイル・実行されます。 -
misc/cgo/test/cgo_test.go
からのTestSetgid
の削除:cgo_test.go
は、Cgoテストスイートの主要なテスト関数を登録するファイルです。ここからTestSetgid
の呼び出しが削除されたことで、汎用的なテスト実行時にsetgid
テストが実行されなくなりました。
これらの変更により、setgid
テストはGoのビルドシステムによって自動的にLinux環境に限定され、他のOSではコンパイルも実行もされなくなります。これにより、setgid
のハングアップ問題に起因するテストの失敗やタイムアウトが、非Linux環境で発生するのを防ぐことができます。
コアとなるコードの変更箇所
misc/cgo/test/basic.go
(削除)
--- a/misc/cgo/test/basic.go
+++ b/misc/cgo/test/basic.go
@@ -11,7 +11,6 @@ package cgotest
#include <stdlib.h>
#include <sys/stat.h>
#include <errno.h>
-#include <unistd.h>
#define SHIFT(x, y) ((x)<<(y))
#define KILO SHIFT(1, 10)
@@ -58,7 +57,6 @@ import "C"
import (
"syscall"
"testing"
- "time"
"unsafe"
)
@@ -126,20 +124,6 @@ func testMultipleAssign(t *testing.T) {
C.free(unsafe.Pointer(p))
}
-func testSetgid(t *testing.T) {
- // Issue 3871.
- c := make(chan bool)
- go func() {
- C.setgid(0)
- c <- true
- }()
- select {
- case <-c:
- case <-time.After(5 * time.Second):
- t.Error("setgid hung")
- }
-}
-
var (
cuint = (C.uint)(0)
culong C.ulong
misc/cgo/test/cgo_linux_test.go
(新規作成)
--- /dev/null
+++ b/misc/cgo/test/cgo_linux_test.go
@@ -0,0 +1,9 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+import "testing"
+
+func TestSetgid(t *testing.T) { testSetgid(t) }
misc/cgo/test/cgo_test.go
(変更)
--- a/misc/cgo/test/cgo_test.go
+++ b/misc/cgo/test/cgo_test.go
@@ -27,6 +27,5 @@ func Test1328(t *testing.T) { test1328(t) }\n func TestParallelSleep(t *testing.T) { testParallelSleep(t) }\n func TestSetEnv(t *testing.T) { testSetEnv(t) }\n func TestHelpers(t *testing.T) { testHelpers(t) }\n-func TestSetgid(t *testing.T) { testSetgid(t) }\n \n func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }\
misc/cgo/test/setgid_linux.go
(新規作成)
--- /dev/null
+++ b/misc/cgo/test/setgid_linux.go
@@ -0,0 +1,32 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that setgid does not hang on GNU/Linux.
+// See http://code.google.com/p/go/issues/detail?id=3871 for details.
+
+package cgotest
+
+/*
+#include <sys/types.h>
+#include <unistd.h>
+*/
+import "C"
+
+import (
+ "testing"
+ "time"
+)
+
+func testSetgid(t *testing.T) {
+ c := make(chan bool)
+ go func() {
+ C.setgid(0)
+ c <- true
+ }()
+ select {
+ case <-c:
+ case <-time.After(5 * time.Second):
+ t.Error("setgid hung")
+ }
+}
コアとなるコードの解説
このコミットの核となる変更は、testSetgid
関数の定義とそのテストエントリポイントを、GoのOS固有のファイル命名規則を利用してLinux環境に限定したことです。
-
testSetgid
関数の移動:basic.go
からtestSetgid
関数が削除され、setgid_linux.go
に移動されました。setgid_linux.go
は、ファイル名に_linux
が含まれているため、GoのビルドシステムによってLinux環境でのみコンパイルされます。これにより、testSetgid
関数自体がLinux専用のコードパスに配置されます。 -
setgid_linux.go
の内容: このファイルには、Cgoを使用してC標準ライブラリのsetgid(0)
を呼び出すロジックが含まれています。setgid(0)
は、プロセスをrootグループIDに設定しようと試みます。この操作は特権を必要としますが、テストの目的は、このシステムコールがハングアップしないことを確認することです。go func() { C.setgid(0); c <- true }()
というゴルーチン内でsetgid(0)
を呼び出し、その完了をチャネルc
で通知します。select
ステートメントは、チャネルからの通知を待つか、5秒のタイムアウトを待つかのいずれか早い方を処理します。もし5秒以内にsetgid
が完了しない場合(ハングアップした場合)、t.Error("setgid hung")
が呼び出され、テストが失敗します。これは、Issue 3871で報告されたハングアップ問題を検出するためのメカニズムです。 -
cgo_linux_test.go
の役割: このファイルは、setgid_linux.go
で定義されたtestSetgid
関数をGoのテストフレームワークに登録するためのものです。func TestSetgid(t *testing.T) { testSetgid(t) }
というシンプルなラッパー関数を提供しています。このファイルも_linux_test.go
というサフィックスを持つため、TestSetgid
テスト関数はLinux環境でのみ実行されます。 -
cgo_test.go
からの削除:cgo_test.go
は、GoのCgoテストスイートのメインのテスト登録ファイルです。ここからTestSetgid(t)
の呼び出しが削除されたことで、Linux以外の環境ではsetgid
テストが完全にスキップされるようになります。
これらの変更により、setgid
システムコールのテストは、その問題が報告されているGNU/Linux環境に限定され、他のOSでのテストの不安定性が解消されます。これは、Goのテストスイートの堅牢性を高めるための重要な改善です。
関連リンク
- Go Issue 3874: https://github.com/golang/go/issues/3874 (このコミットが修正したとされるIssue)
- Go Issue 3871: https://github.com/golang/go/issues/3871 (
setgid
がハングアップする問題の元の報告) - Go Code Review 6446060: https://golang.org/cl/6446060 (このコミットのGo Code Reviewページ)
参考にした情報源リンク
- Go言語の公式ドキュメント (Cgo): https://go.dev/blog/c-go-is-not-go
setgid
システムコールに関するmanページ (Linux):man 2 setgid
(通常、Linuxシステム上でコマンドラインから参照可能)- Goのテストパッケージに関するドキュメント: https://pkg.go.dev/testing
- Goのビルド制約 (Build Constraints) に関するドキュメント: https://pkg.go.dev/go/build (特にファイル名サフィックスによるOS/アーキテクチャ指定について)
- Go Issue Tracker: https://github.com/golang/go/issues
- Go Code Review: https://go.dev/doc/contribute#code_reviews