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

[インデックス 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に限定している点にあります。

  1. misc/cgo/test/basic.goからのtestSetgid関数の削除: 元のbasic.goファイルは、GoのCgoテストスイートの基本的なテストケースをまとめたものでした。このファイルからtestSetgid関数が削除されたことで、このテストが汎用的なテストスイートの一部として実行されなくなりました。

  2. misc/cgo/test/setgid_linux.goの新規作成: setgid_linux.goという新しいファイルが作成され、削除されたtestSetgid関数がこのファイルに移動されました。Goのビルドシステムは、ファイル名に_linux.goというサフィックスが付いている場合、そのファイルをLinux環境でのみコンパイルします。これにより、testSetgidはLinux環境でのみ実行されるようになります。

    このファイルには、setgid(0)を呼び出すゴルーチンを起動し、その完了をチャネルで待機するロジックが含まれています。タイムアウト(5秒)を設定することで、setgidがハングアップした場合にテストが無限に待機するのを防ぎ、エラーとして検出できるようにしています。

  3. misc/cgo/test/cgo_linux_test.goの新規作成: このファイルは、setgid_linux.goで定義されたtestSetgid関数を呼び出すためのテストエントリポイントを提供します。func TestSetgid(t *testing.T) { testSetgid(t) }という形式で、Goのテストフレームワークが認識するテスト関数を定義しています。このファイルも_linux_test.goというサフィックスを持つため、Linux環境でのみコンパイル・実行されます。

  4. 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環境に限定したことです。

  1. testSetgid関数の移動: basic.goからtestSetgid関数が削除され、setgid_linux.goに移動されました。setgid_linux.goは、ファイル名に_linuxが含まれているため、GoのビルドシステムによってLinux環境でのみコンパイルされます。これにより、testSetgid関数自体がLinux専用のコードパスに配置されます。

  2. 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で報告されたハングアップ問題を検出するためのメカニズムです。

  3. cgo_linux_test.goの役割: このファイルは、setgid_linux.goで定義されたtestSetgid関数をGoのテストフレームワークに登録するためのものです。func TestSetgid(t *testing.T) { testSetgid(t) }というシンプルなラッパー関数を提供しています。このファイルも_linux_test.goというサフィックスを持つため、TestSetgidテスト関数はLinux環境でのみ実行されます。

  4. cgo_test.goからの削除: cgo_test.goは、GoのCgoテストスイートのメインのテスト登録ファイルです。ここからTestSetgid(t)の呼び出しが削除されたことで、Linux以外の環境ではsetgidテストが完全にスキップされるようになります。

これらの変更により、setgidシステムコールのテストは、その問題が報告されているGNU/Linux環境に限定され、他のOSでのテストの不安定性が解消されます。これは、Goのテストスイートの堅牢性を高めるための重要な改善です。

関連リンク

参考にした情報源リンク