[インデックス 15592] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにおけるOpenBSD 5.2でのUnixドメインソケットに対するgetsockname
システムコールの挙動に関するバグ修正です。具体的には、OpenBSD 5.2でバインドされていないUnixドメインソケットに対してgetsockname
を呼び出した際に発生する、アドレスファミリー(AF)が未設定で長さがゼロとして返される問題へのワークアラウンドを実装しています。これにより、TestPassFD
テストがOpenBSD 5.2で正しく動作するようになります。
コミット
commit 1b36bcc3b59a3e123c5b1ac2617cadcb69d0e94a
Author: Joel Sing <jsing@google.com>
Date: Tue Mar 5 21:40:37 2013 +1100
syscall: handle getsockname for unix sockets on openbsd 5.2
On OpenBSD 5.2, calling getsockname on an unbound Unix domain socket
results in a successful syscall, however the AF is unset and the length
is returned as zero. This has been changed to more portable behaviour,
which will be included in the OpenBSD 5.3 release.
For now, work around this by treating a successful getsockname() call
that returns a family of AF_UNSPEC and length of zero as a AF_UNIX
socket.
Makes TestPassFD work on OpenBSD 5.2.
Fixes #4956.
R=golang-dev, minux.ma, rsc, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/7449046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1b36bcc3b59a3e123c5b1ac2617cadcb69d0e94a
元コミット内容
このコミットは、OpenBSD 5.2におけるUnixドメインソケットのgetsockname
の挙動を修正します。OpenBSD 5.2では、バインドされていないUnixドメインソケットに対してgetsockname
を呼び出すと、システムコール自体は成功するものの、アドレスファミリー(AF)が未設定(AF_UNSPEC
)で、長さがゼロとして返されるという問題がありました。この挙動はOpenBSD 5.3でよりポータブルな挙動に変更される予定でしたが、それまでの間、Goのsyscall
パッケージでこの特定のケース(AF_UNSPEC
かつ長さゼロ)をAF_UNIX
ソケットとして扱うことでワークアラウンドを適用しています。これにより、TestPassFD
テストがOpenBSD 5.2で正しく動作するようになります。
変更の背景
この変更の背景には、OpenBSD 5.2の特定のバージョンにおけるgetsockname
システムコールの非標準的な挙動があります。通常、getsockname
はソケットにバインドされたローカルアドレスを返すために使用されます。Unixドメインソケットの場合、これはファイルシステム上のパス、または抽象的な名前空間内の名前になります。
OpenBSD 5.2では、ソケットがまだbind()
されていない状態でgetsockname()
を呼び出すと、システムコール自体はエラーを返さずに成功します。しかし、返されるsockaddr
構造体のアドレスファミリーフィールド(sa_family
)がAF_UNSPEC
(未指定)となり、アドレスの長さ(sa_len
)がゼロとして返されるという、予期せぬ状態になっていました。これは、他の多くのUNIX系システムや、OpenBSDの将来のバージョン(5.3以降)での標準的な挙動とは異なっていました。
この非標準的な挙動が、Go言語のsyscall
パッケージ内のTestPassFD
テストの失敗を引き起こしていました。TestPassFD
はファイルディスクリプタの受け渡しをテストするもので、内部的にUnixドメインソケットを使用しています。このテストがOpenBSD 5.2で失敗するのは、getsockname
がソケットの正しい情報を返さないため、ソケットのタイプを正しく識別できないことが原因でした。
この問題を解決するため、OpenBSD 5.3で導入される予定の「よりポータブルな挙動」が適用されるまでの間、Go側でこの特定のケースを検出して補正するワークアラウンドが必要となりました。
前提知識の解説
1. システムコール (System Call)
システムコールは、ユーザー空間のプログラムがオペレーティングシステム(OS)のカーネル空間の機能にアクセスするためのインターフェースです。ファイルI/O、プロセス管理、ネットワーク通信など、OSが提供する低レベルな操作を実行するために使用されます。getsockname
もその一つで、ソケットに関する情報を取得するためのシステムコールです。
2. ソケット (Socket)
ソケットは、ネットワーク通信やプロセス間通信(IPC)のエンドポイントとして機能する抽象化された概念です。プログラムはソケットを通じてデータを送受信します。ソケットには様々な種類があり、通信プロトコルやアドレスファミリーによって分類されます。
3. Unixドメインソケット (Unix Domain Socket / UDS)
Unixドメインソケットは、同じホスト上のプロセス間で通信を行うためのソケットの一種です。TCP/IPソケットがネットワークを介した通信に使用されるのに対し、Unixドメインソケットはファイルシステム上のパス(例: /tmp/my_socket
)をアドレスとして使用し、カーネル内で直接データをやり取りするため、ネットワークソケットよりも高速でオーバーヘッドが少ないという特徴があります。
4. getsockname()
システムコール
getsockname()
は、指定されたソケットのローカルアドレス(ソケットがバインドされているアドレス)を取得するためのシステムコールです。例えば、TCPソケットであればローカルIPアドレスとポート番号、Unixドメインソケットであればバインドされているファイルパスなどが返されます。
5. sockaddr
構造体
sockaddr
は、ソケットアドレスを表現するための汎用的な構造体です。異なる種類のアドレスファミリー(IPv4、IPv6、Unixドメインなど)に対応するために、通常はsockaddr_in
(IPv4)、sockaddr_in6
(IPv6)、sockaddr_un
(Unixドメイン)などの具体的な構造体にキャストして使用されます。
sa_family
: アドレスファミリー(例:AF_INET
、AF_INET6
、AF_UNIX
、AF_UNSPEC
)。sa_len
: アドレスの長さ(OpenBSDなど一部のBSD系システムで使用される)。
6. AF_UNSPEC
と AF_UNIX
AF_UNSPEC
: アドレスファミリーが未指定であることを示す定数です。通常、ソケットがまだアドレスにバインドされていない場合や、アドレスファミリーが不明な場合に使用されます。AF_UNIX
: Unixドメインソケットのアドレスファミリーを示す定数です。
7. TestPassFD
Go言語のテストスイートに含まれる特定のテストケースで、ファイルディスクリプタ(FD)のプロセス間での受け渡し機能が正しく動作するかどうかを検証します。この機能は、UnixドメインソケットのSCM_RIGHTS
メッセージを介して実現されます。
8. OpenBSD
OpenBSDは、セキュリティとコードの品質に重点を置いたUNIX系オペレーティングシステムです。厳格な開発プロセスとコードレビューで知られています。OSのバージョンアップに伴い、システムコールの挙動が改善されることがあります。
技術的詳細
このコミットは、OpenBSD 5.2におけるgetsockname
システムコールの特定の挙動に対するGo言語のsyscall
パッケージの適応を示しています。
通常、getsockname
はソケットのローカルアドレス情報をsockaddr
構造体として返します。Unixドメインソケットの場合、この情報はsockaddr_un
構造体として解釈され、sun_family
フィールドにはAF_UNIX
が、sun_path
フィールドにはバインドされたパスが格納されます。
しかし、OpenBSD 5.2では、バインドされていないUnixドメインソケットに対してgetsockname
を呼び出すと、以下の問題が発生しました。
- システムコール自体は成功する(エラーが返されない)。
- 返される
sockaddr
構造体のsa_family
フィールドがAF_UNSPEC
(未指定)となる。 - 返される
sockaddr
構造体のsa_len
フィールドが0
となる。
この挙動は、Goのsyscall
パッケージがソケットのタイプを正しく識別するのを妨げ、特にTestPassFD
のようなUnixドメインソケットの機能に依存するテストが失敗する原因となっていました。
OpenBSDの開発者はこの挙動を認識しており、OpenBSD 5.3でよりポータブルな(他のUNIX系システムと互換性のある)挙動に変更することを決定していました。このコミットは、OpenBSD 5.3が広く利用可能になるまでの間、OpenBSD 5.2での互換性を確保するためのワークアラウンドです。
ワークアラウンドのロジックは以下の通りです。
getsockname
システムコールが成功したにもかかわらず、- 返された
sockaddr
構造体のアドレスファミリーがAF_UNSPEC
であり、 - かつ、アドレスの長さが
0
である場合、 - そのソケットはOpenBSD 5.2のバインドされていないUnixドメインソケットであると判断し、
- 強制的にアドレスファミリーを
AF_UNIX
に、アドレスの長さをSizeofSockaddrUnix
(Unixドメインソケットアドレスの標準的なサイズ)に設定し直します。
これにより、Goのsyscall
パッケージは、OpenBSD 5.2の非標準的なgetsockname
の戻り値でも、Unixドメインソケットを正しく認識し、関連する機能(TestPassFD
など)が期待通りに動作するようになります。
コミットメッセージには「TODO(jsing): Remove after OpenBSD 5.4 is released (see issue 3349).」というコメントがあり、これはこのワークアラウンドが一時的なものであることを示しています。OpenBSD 5.4以降では、この特定のコードは不要になる予定でした。
コアとなるコードの変更箇所
このコミットでは、主に以下の2つのファイルが変更されています。
-
src/pkg/syscall/passfd_test.go
TestPassFD
関数から、OpenBSDに関する特定のスキップロジックが削除されました。- 変更前:
if runtime.GOOS == "openbsd" { t.Skip("issue 4956") }
- 変更後: このブロックが完全に削除されています。
-
src/pkg/syscall/syscall_bsd.go
Getsockname
関数内に、OpenBSD 5.2の挙動を補正するロジックが追加されました。- 変更前:
func Getsockname(fd int) (sa Sockaddr, err error) { var rsa RawSockaddrAny var len _Socklen if err = getsockname(fd, &rsa, &len); err != nil { return } return anyToSockaddr(&rsa) }
- 変更後:
func Getsockname(fd int) (sa Sockaddr, err error) { var rsa RawSockaddrAny var len _Socklen if err = getsockname(fd, &rsa, &len); err != nil { return } // TODO(jsing): Remove after OpenBSD 5.4 is released (see issue 3349). if runtime.GOOS == "openbsd" && rsa.Addr.Family == AF_UNSPEC && rsa.Addr.Len == 0 { rsa.Addr.Family = AF_UNIX rsa.Addr.Len = SizeofSockaddrUnix } return anyToSockaddr(&rsa) }
コアとなるコードの解説
src/pkg/syscall/passfd_test.go
の変更
TestPassFD
関数は、ファイルディスクリプタの受け渡し機能(UnixドメインソケットのSCM_RIGHTS
メッセージを介して実現される)をテストするためのものです。以前は、OpenBSD上でこのテストが失敗する既知の問題(issue 4956)があったため、runtime.GOOS == "openbsd"
の場合にテストをスキップするロジックが含まれていました。
このコミットでsyscall_bsd.go
に修正が加えられたことにより、OpenBSD 5.2でもgetsockname
が正しく動作するようになったため、このスキップロジックは不要となり削除されました。これにより、TestPassFD
がOpenBSD 5.2でも実行され、機能が正しく動作することが検証されるようになりました。
src/pkg/syscall/syscall_bsd.go
の変更
このファイルは、BSD系のオペレーティングシステム(OpenBSDを含む)向けのシステムコール関連の定義と実装を含んでいます。
Getsockname
関数は、Goのsyscall
パッケージが提供するgetsockname
システムコールのラッパーです。この関数は、ソケットディスクリプタfd
を受け取り、そのソケットのローカルアドレスをSockaddr
インターフェースとして返します。
追加されたコードブロックは以下の通りです。
// TODO(jsing): Remove after OpenBSD 5.4 is released (see issue 3349).
if runtime.GOOS == "openbsd" && rsa.Addr.Family == AF_UNSPEC && rsa.Addr.Len == 0 {
rsa.Addr.Family = AF_UNIX
rsa.Addr.Len = SizeofSockaddrUnix
}
runtime.GOOS == "openbsd"
: この条件は、現在の実行環境がOpenBSDである場合にのみ、以下のロジックが適用されることを保証します。これはOS固有のワークアラウンドであるため、他のOSには影響を与えません。rsa.Addr.Family == AF_UNSPEC
:getsockname
が返したsockaddr
構造体のアドレスファミリーがAF_UNSPEC
(未指定)であるかどうかをチェックします。これはOpenBSD 5.2の非標準的な挙動の特徴です。rsa.Addr.Len == 0
:getsockname
が返したsockaddr
構造体のアドレスの長さが0
であるかどうかをチェックします。これもOpenBSD 5.2の非標準的な挙動の特徴です。
これら3つの条件がすべて真である場合、GoランタイムはOpenBSD 5.2のバインドされていないUnixドメインソケットの特殊なケースであると判断します。
rsa.Addr.Family = AF_UNIX
:sockaddr
構造体のアドレスファミリーをAF_UNIX
に強制的に設定し直します。これにより、Goのsyscall
パッケージがこのソケットをUnixドメインソケットとして正しく認識できるようになります。rsa.Addr.Len = SizeofSockaddrUnix
:sockaddr
構造体のアドレスの長さをSizeofSockaddrUnix
に設定し直します。SizeofSockaddrUnix
は、Unixドメインソケットのアドレス構造体(sockaddr_un
)の標準的なサイズを示す定数です。これにより、アドレスの長さも正しい値に補正されます。
この修正により、OpenBSD 5.2上でもGetsockname
関数がUnixドメインソケットに対して期待される正しい情報を返すようになり、TestPassFD
を含む関連する機能が正常に動作するようになりました。TODO
コメントは、このワークアラウンドがOpenBSDの将来のバージョンで不要になることを示唆しており、コードの保守性に関する考慮がなされています。
関連リンク
- Go issue 4956: https://github.com/golang/go/issues/4956
- Go issue 3349: https://github.com/golang/go/issues/3349
- Go CL 7449046: https://golang.org/cl/7449046
参考にした情報源リンク
getsockname
man page (Linux): https://man7.org/linux/man-pages/man2/getsockname.2.html- Unix Domain Sockets (Wikipedia): https://en.wikipedia.org/wiki/Unix_domain_socket
- OpenBSD official website: https://www.openbsd.org/
- Go
syscall
package documentation: https://pkg.go.dev/syscall - Go
runtime
package documentation: https://pkg.go.dev/runtime sockaddr
structure: https://www.gnu.org/software/libc/manual/html_node/Socket-Addresses.htmlAF_UNSPEC
andAF_UNIX
constants: https://man7.org/linux/man-pages/man2/socket.2.htmlSCM_RIGHTS
(for file descriptor passing): https://man7.org/linux/man-pages/man7/unix.7.html- OpenBSD 5.3 release notes (relevant changes to
getsockname
behavior): (具体的なリンクはOpenBSDのアーカイブから探す必要がありますが、一般的にリリースノートにシステムコールの変更が記載されます。) - OpenBSD
sockaddr
structure (OpenBSD specificsa_len
field): (OpenBSDのmanページやソースコードを参照) - Go issue 3349 (related to
getsockname
andAF_UNSPEC
on OpenBSD): https://github.com/golang/go/issues/3349 - Go issue 4956 (TestPassFD failing on OpenBSD): https://github.com/golang/go/issues/4956
- Go CL 7449046 (Gerrit code review for this commit): https://golang.org/cl/7449046