[インデックス 18639] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにMmap
およびMunmap
システムコールをSyscall9
経由で呼び出すテストを追加するものです。このテストは、アセンブリフラグメントの破損を検出するための「カナリア」として機能することを目的としています。
コミット
- コミットハッシュ:
b05f3de56f018370ce347c2e565ce16cd724f7c3
- Author: Mikio Hara mikioh.mikioh@gmail.com
- Date: Tue Feb 25 23:02:19 2014 +0900
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b05f3de56f018370ce347c2e565ce16cd724f7c3
元コミット内容
syscall: add mmap test
This CL adds a test that calls Mmap and Munmap through Syscall9
as the canary that detects assembly fragment breakage. For now
there is no package test that uses Syscall9 in the standard
library across all Unix-like systems.
Note that the package runtime owns its assembly fragments, so
this canary never works for runtime breakage.
LGTM=iant, bradfitz
R=iant, minux.ma, bradfitz
CC=golang-codereviews
https://golang.org/cl/61520049
変更の背景
この変更の主な背景は、Go言語の標準ライブラリにおいて、Syscall9
という低レベルなシステムコール呼び出しメカニズムを使用するテストが不足していた点にあります。特に、Unix系システム全体でSyscall9
を使用するパッケージテストが存在しない状況でした。
Go言語の内部では、特定の最適化やプラットフォーム固有の処理のためにアセンブリコードが使用されることがあります。これらのアセンブリコードは「アセンブリフラグメント」と呼ばれ、Goのコンパイラやリンカによって生成されるバイナリの一部となります。アセンブリフラグメントが破損すると、プログラムの予期せぬ動作やクラッシュにつながる可能性があります。
このコミットは、Syscall9
を介してMmap
とMunmap
というメモリ管理に関連するシステムコールを呼び出すテストを追加することで、このようなアセンブリフラグメントの破損を早期に検出するための「カナリア」として機能させることを目的としています。カナリアとは、システムが正常に機能しているかを確認するためのシンプルなテストや指標を指します。
ただし、コミットメッセージにもあるように、runtime
パッケージが自身のアセンブリフラグメントを管理しているため、このテストはruntime
パッケージ内部の破損を検出するようには設計されていません。これは、runtime
パッケージがGoプログラムの実行環境そのものを担当しており、その低レベルな性質上、通常のsyscall
パッケージのテストとは異なるメカニズムが必要となるためです。
前提知識の解説
このコミットを理解するためには、以下の概念について理解しておく必要があります。
-
syscall
パッケージ: Go言語のsyscall
パッケージは、オペレーティングシステム(OS)の低レベルなプリミティブへのインターフェースを提供します。これにより、GoプログラムはファイルI/O、プロセス管理、ネットワーク通信などの特権的な操作をOSカーネルに直接要求するシステムコールを呼び出すことができます。これは、Goの標準ライブラリが提供する高レベルなAPI(例:os
パッケージのファイル操作)の基盤となっています。 -
システムコール: プログラムがOSカーネルに特定のサービスを要求するためのメカニズムです。例えば、ファイルの読み書き、メモリの確保、プロセスの作成などがシステムコールを通じて行われます。システムコールはOSとアプリケーションの間の重要な境界であり、セキュリティと安定性を確保するために厳密に管理されています。
-
Mmap
とMunmap
:Mmap
(Memory Map): ファイルやデバイスをプロセスのアドレス空間にマッピング(割り当て)するためのシステムコールです。これにより、ファイルの内容をメモリのように直接アクセスできるようになります。また、匿名メモリ領域(ファイルに関連付けられていないメモリ)を確保するためにも使用されます。Munmap
(Memory Unmap):Mmap
によってマッピングされたメモリ領域を解放するためのシステムコールです。
-
Syscall9
:syscall
パッケージ内で提供される関数の一つで、最大9つの引数を取るシステムコールを呼び出すために使用されます。そのシグネチャはfunc Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
です。num
: システムコール番号。OSとアーキテクチャに固有の値です。a1
〜a9
: システムコールに渡される引数。通常はuintptr
型で、メモリアドレスや整数値を表します。r1
,r2
: システムコールからの戻り値。err
: エラーを示すErrno
型。Syscall9
のような低レベルな関数は、OSの内部動作に深く関わるため、プラットフォーム間で動作が大きく異なる可能性があります。Go 1.4以降、syscall
パッケージは「ロックダウン」され、新しいコードではgolang.org/x/sys
パッケージの使用が推奨されています。また、Go 1.18では可変長引数を取るSyscallN
が追加され、Syscall9
は非推奨となっています。
-
アセンブリフラグメント: Go言語のコンパイラは、特定の最適化やOSとのインターフェースのために、Goコードの一部を直接アセンブリコードに変換したり、既存のアセンブリコードを組み込んだりすることがあります。これらのアセンブリコードの断片を「アセンブリフラグメント」と呼びます。これらはGoのランタイムや標準ライブラリの低レベルな部分で特に重要です。
-
カナリアテスト (Canary Test): 鉱山で有毒ガスを検出するためにカナリアを使用していたことに由来する用語です。ソフトウェア開発においては、システム全体の健全性を監視するために、特定の脆弱な部分や重要な機能に対して行われる小規模で独立したテストを指します。このテストが失敗した場合、より広範な問題が発生している可能性を示唆します。
技術的詳細
このコミットで追加されたテストは、syscall
パッケージのTestMmap
関数です。この関数は、syscall.Mmap
とsyscall.Munmap
を呼び出すことで、メモリマッピングとアンマッピングの基本的な機能が正しく動作するかを確認します。
特筆すべきは、このテストがSyscall9
を直接使用しているわけではない点です。コミットメッセージの「calls Mmap and Munmap through Syscall9」という記述は、syscall.Mmap
やsyscall.Munmap
といった高レベルなsyscall
パッケージの関数が、内部的にSyscall9
(またはそれに類する低レベルなシステムコールラッパー)を呼び出していることを示唆しています。つまり、このテストはsyscall
パッケージの公開APIを通じてMmap
とMunmap
をテストしていますが、その裏でSyscall9
が使われているため、Syscall9
が関与するアセンブリフラグメントの健全性を間接的に検証している、という意図です。
テストの具体的な内容は以下の通りです。
syscall.Mmap(-1, 0, syscall.Getpagesize(), syscall.PROT_NONE, syscall.MAP_ANON|syscall.MAP_PRIVATE)
を呼び出します。-1
: ファイルディスクリプタ。通常、匿名メモリマッピング(ファイルに関連付けないメモリ)を作成する際に使用されます。0
: オフセット。匿名マッピングでは通常0です。syscall.Getpagesize()
: OSのメモリページサイズを取得します。このサイズのメモリ領域を確保します。syscall.PROT_NONE
: ページへのアクセス権限を設定します。PROT_NONE
はアクセスを許可しないことを意味します。これは、単にメモリ領域を確保し、後で解放できることを確認するためのテストであるため、アクセス権限は重要ではありません。syscall.MAP_ANON|syscall.MAP_PRIVATE
: マッピングのフラグを設定します。MAP_ANON
: ファイルに関連付けられていない匿名メモリ領域を作成します。MAP_PRIVATE
: プライベートマッピングを作成します。これにより、このプロセスが行うメモリへの変更は他のプロセスには見えません。
Mmap
の呼び出しがエラーを返した場合、テストは失敗します。Mmap
が成功した場合、返されたメモリ領域b
をsyscall.Munmap(b)
で解放します。Munmap
の呼び出しがエラーを返した場合も、テストは失敗します。
このテストは、Mmap
とMunmap
という基本的なメモリ管理システムコールが、Syscall9
を介した低レベルな呼び出しパスを含めて、正しく機能していることを確認します。これにより、Goのコンパイラやリンカが生成するアセンブリフラグメントに予期せぬ破損がないかを検出する「カナリア」としての役割を果たします。
ただし、コミットメッセージにあるように、「runtime
パッケージが自身のアセンブリフラグメントを所有しているため、このカナリアはランタイムの破損には決して機能しない」という重要な注意点があります。runtime
パッケージはGoプログラムの実行時環境を管理する非常に低レベルな部分であり、ガベージコレクション、スケジューラ、スタック管理など、OSとの密接な連携を必要とする機能を含んでいます。これらの機能は、syscall
パッケージとは異なる、より特殊なアセンブリコードやOS固有のインターフェースを使用しているため、syscall
パッケージのテストではその健全性を完全に保証することはできません。
コアとなるコードの変更箇所
--- a/src/pkg/syscall/syscall_unix_test.go
+++ b/src/pkg/syscall/syscall_unix_test.go
@@ -77,6 +77,16 @@ func TestFcntlFlock(t *testing.T) {
}\n
}\n
\n
+func TestMmap(t *testing.T) {
+\tb, err := syscall.Mmap(-1, 0, syscall.Getpagesize(), syscall.PROT_NONE, syscall.MAP_ANON|syscall.MAP_PRIVATE)\n
+\tif err != nil {
+\t\tt.Fatalf(\"Mmap: %v\", err)\n
+\t}\n
+\tif err := syscall.Munmap(b); err != nil {
+\t\tt.Fatalf(\"Munmap: %v\", err)\n
+\t}\n
+}\n
+\n
// TestPassFD tests passing a file descriptor over a Unix socket.\n
//\n
// This test involved both a parent and child process. The parent\n
コアとなるコードの解説
追加されたコードは、src/pkg/syscall/syscall_unix_test.go
ファイル内のTestMmap
関数です。
func TestMmap(t *testing.T) {
// Mmapを呼び出して、匿名でプライベートなメモリ領域を確保する
// -1: ファイルディスクリプタ(匿名マッピングのため)
// 0: オフセット
// syscall.Getpagesize(): OSのページサイズ分のメモリを確保
// syscall.PROT_NONE: アクセス権限なし(テスト目的のため)
// syscall.MAP_ANON|syscall.MAP_PRIVATE: 匿名かつプライベートなマッピング
b, err := syscall.Mmap(-1, 0, syscall.Getpagesize(), syscall.PROT_NONE, syscall.MAP_ANON|syscall.MAP_PRIVATE)
if err != nil {
// Mmapが失敗した場合、テストを失敗させる
t.Fatalf("Mmap: %v", err)
}
// 確保したメモリ領域をMunmapで解放する
if err := syscall.Munmap(b); err != nil {
// Munmapが失敗した場合、テストを失敗させる
t.Fatalf("Munmap: %v", err)
}
}
このテスト関数は、以下の手順を実行します。
syscall.Mmap
関数を呼び出し、OSから匿名でプライベートなメモリページを1ページ分(syscall.Getpagesize()
で取得されるサイズ)確保しようとします。この際、アクセス権限はPROT_NONE
(アクセス不可)に設定されています。これは、実際にメモリを読み書きするのではなく、単にメモリの確保と解放のメカニズムが機能するかどうかを確認するためです。Mmap
の呼び出しがエラーを返した場合、t.Fatalf
を使用してテストを即座に失敗させ、エラーメッセージを出力します。Mmap
が成功し、メモリ領域b
が返された場合、次にsyscall.Munmap
関数を呼び出して、そのメモリ領域を解放します。Munmap
の呼び出しがエラーを返した場合も、同様にt.Fatalf
でテストを失敗させます。
このシンプルなテストは、Mmap
とMunmap
という重要なシステムコールが、Goのsyscall
パッケージを通じて正しく呼び出され、期待通りに動作することを確認します。特に、これらの関数が内部的に利用する可能性のあるSyscall9
のような低レベルなアセンブリフラグメントが、コンパイラやリンカの変更によって破損していないかを検出する役割を担っています。
関連リンク
- Go CL 61520049: https://golang.org/cl/61520049
参考にした情報源リンク
- Go言語の
syscall
パッケージに関するドキュメント - Go言語の
syscall.Syscall9
に関する情報 - Go言語の
golang.org/x/sys
パッケージに関する情報 - Mmapシステムコールに関する一般的な情報
- Munmapシステムコールに関する一般的な情報
- Go Syscall9 - Web Search Results
- Go Syscall9 - Web Search Results (additional)
- Go Syscall9 - Web Search Results (additional)
- Go Syscall9 - Web Search Results (additional)
- Go Syscall9 - Web Search Results (additional)
- Go Syscall9 - Web Search Results (additional)