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

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

このコミットは、Go言語のsyscallパッケージにおけるファイルディスクリプタ(FD)の受け渡しに関するバグ修正です。特にFreeBSDとNetBSDというUnix系OS上での挙動に焦点を当て、コントロールメッセージ(Cmsg)のデータ部分へのポインタ計算におけるアライメントの問題を解決しています。これにより、これらのOSでソケット経由でのFD受け渡しが正しく機能するようになります。

コミット

commit 98d44d140d7abde9fdfbdbf7adec5be7bb0892ce
Author: Dave Cheney <dave@cheney.net>
Date:   Wed Feb 27 09:13:15 2013 +1100

    syscall: fix FD passing on FreeBSD and NetBSD
    
    Fixes #3348.
    
    R=devon.odell, minux.ma, bradfitz, mdempsky
    CC=golang-dev
    https://golang.org/cl/7406050

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

https://github.com/golang/go/commit/98d44d140d7abde9fdfbdbf7adec5be7bb0892ce

元コミット内容

syscall: fix FD passing on FreeBSD and NetBSD

Fixes #3348.

R=devon.odell, minux.ma, bradfitz, mdempsky
CC=golang-dev
https://golang.org/cl/7406050

変更の背景

Go言語のsyscallパッケージは、OSのシステムコールをGoプログラムから呼び出すためのインターフェースを提供します。ファイルディスクリプタ(FD)の受け渡しは、プロセス間通信(IPC)において、あるプロセスがオープンしているファイルやソケットのFDを別のプロセスに安全に渡すための重要な機能です。これは通常、Unixドメインソケットを介してコントロールメッセージ(SCM_RIGHTS)として行われます。

Goの初期バージョンでは、FreeBSDやNetBSDといった一部のUnix系OSにおいて、このFD受け渡し機能が正しく動作しないというバグ(Issue #3348)が報告されていました。この問題は、コントロールメッセージの構造体(Cmsghdr)の後に続くデータ部分のアライメント(メモリ配置の制約)が、OSによって異なるために発生していました。特に、FreeBSDやNetBSDでは、データ部分が特定のバイト境界にアライメントされている必要がありましたが、Goの既存の実装ではこのアライメントが考慮されていなかったため、不正なメモリアクセスやデータの破損が発生し、FDの受け渡しが失敗していました。

このコミットは、このFD受け渡しに関するバグを修正し、GoプログラムがFreeBSDおよびNetBSD上でも安定してFDをやり取りできるようにすることを目的としています。

前提知識の解説

1. ファイルディスクリプタ(File Descriptor, FD)

ファイルディスクリプタは、Unix系OSにおいて、プロセスがファイル、ソケット、パイプなどのI/Oリソースを識別するために使用する整数値です。プロセスはFDを通じてこれらのリソースにアクセスします。

2. プロセス間通信(Inter-Process Communication, IPC)

異なるプロセス間でデータや情報を交換するメカニズムの総称です。FDの受け渡しは、IPCの一種であり、特に特権を伴うリソースの共有に利用されます。

3. Unixドメインソケット

同じホスト上のプロセス間での通信に特化したソケットの一種です。ネットワークソケットとは異なり、ファイルシステム上のパス名で識別されます。高速で安全なIPC手段として広く利用されます。

4. コントロールメッセージ(Control Message)

ソケット通信において、通常のデータとは別に、補助的な情報(例: ファイルディスクリプタ、認証情報、帯域外データ)をやり取りするためのメカニズムです。sendmsg()recvmsg()システムコールで送受信されます。

5. Cmsghdr構造体

コントロールメッセージのヘッダ部分を定義する構造体です。メッセージのタイプ(例: SCM_RIGHTS)、長さ、レベルなどの情報を含みます。このヘッダの直後に、実際のコントロールデータ(例: 渡されるFDのリスト)が続きます。

6. SCM_RIGHTS

ファイルディスクリプタをコントロールメッセージとして送受信する際に使用されるコントロールメッセージタイプです。

7. メモリアライメント(Memory Alignment)

コンピュータのメモリ上でデータが配置される際の、特定のバイト境界への制約です。CPUは、特定のデータ型(例: 整数、ポインタ)が特定のバイト境界(例: 4バイト、8バイト)に配置されている場合に、より効率的にアクセスできます。アライメントが正しくないと、パフォーマンスの低下や、最悪の場合、不正なメモリアクセスによるプログラムのクラッシュを引き起こす可能性があります。特に、異なるアーキテクチャやOSでは、アライメントの要件が異なることがあります。

8. unsafeパッケージ(Go言語)

Go言語のunsafeパッケージは、型安全性をバイパスして、ポインタ演算や型変換を可能にする機能を提供します。これは、C言語との相互運用や、低レベルのシステムプログラミング(syscallパッケージなど)で必要となることがありますが、誤用するとメモリ破壊や未定義動作を引き起こす可能性があるため、慎重に使用する必要があります。

  • unsafe.Pointer: 任意の型のポインタを保持できる汎用ポインタ。
  • uintptr: ポインタを整数として扱うことができる型。ポインタ演算を行う際に使用されます。

技術的詳細

このコミットの核心は、Cmsghdr構造体の直後に続くコントロールデータのアドレス計算にあります。Unix系OSでは、Cmsghdrのサイズは固定ですが、その後に続くデータ部分が特定のバイト境界にアライメントされている必要があります。このアライメント要件はOSによって異なり、単にCmsghdrのサイズを足すだけでは不十分な場合があります。

元のコードでは、cmsgData関数がコントロールデータの開始アドレスを計算する際に、Cmsghdrのサイズ(SizeofCmsghdr)を単純に現在のポインタに加算していました。

return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + SizeofCmsghdr)

しかし、FreeBSDやNetBSDのようなシステムでは、Cmsghdrの直後のデータが、そのシステムのポインタサイズやデータ型の最大アライメント要件に合わせてパディングされる必要があります。このパディングを考慮しないと、データ部分の開始アドレスが不正なアライメントになり、FDの受け渡しが失敗する原因となります。

修正後のコードでは、cmsgAlignOfという新しい関数が導入され、SizeofCmsghdrをこのアライメント関数に通すことで、OSが要求する適切なアライメントを考慮したオフセットを計算しています。

return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(SizeofCmsghdr)))

cmsgAlignOf関数は、おそらくOS固有のアライメント要件(例えば、CMSG_ALIGNマクロに相当するロジック)をカプセル化しており、与えられたサイズを次の適切なアライメント境界に切り上げる役割を果たします。これにより、Cmsghdrの直後のデータが常に正しくアライメントされたアドレスから始まることが保証され、FDの受け渡しが安定して行われるようになります。

また、passfd_test.goのビルドタグにfreebsdnetbsdが追加されたことは、この修正がこれらのOSでテストされることを意味しており、修正の有効性を確認するための重要なステップです。

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

このコミットによる主要なコード変更は以下の2ファイルです。

  1. src/pkg/syscall/passfd_test.go
  2. src/pkg/syscall/sockcmsg_unix.go

src/pkg/syscall/passfd_test.go

--- a/src/pkg/syscall/passfd_test.go
+++ b/src/pkg/syscall/passfd_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build linux darwin
+// +build linux darwin freebsd netbsd
 
 package syscall_test

src/pkg/syscall/sockcmsg_unix.go

--- a/src/pkg/syscall/sockcmsg_unix.go
+++ b/src/pkg/syscall/sockcmsg_unix.go
@@ -37,7 +37,7 @@ func CmsgSpace(datalen int) int {
 }
 
 func cmsgData(h *Cmsghdr) unsafe.Pointer {
-	return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + SizeofCmsghdr)
+	return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(SizeofCmsghdr)))
 }
 
 // SocketControlMessage represents a socket control message.

コアとなるコードの解説

src/pkg/syscall/passfd_test.goの変更

このファイルは、ファイルディスクリプタの受け渡し機能をテストするためのコードを含んでいます。変更前はlinuxdarwin(macOS)でのみビルドされるように指定されていました。

// +build linux darwin

この行が以下のように変更されました。

// +build linux darwin freebsd netbsd

これは、passfd_test.goに含まれるテストコードが、今後はFreeBSDとNetBSD上でもコンパイルされ、実行されるべきであることを示しています。これにより、今回のFD受け渡しに関する修正が、これらのOSで正しく機能するかどうかを自動的に検証できるようになります。これは、クロスプラットフォームな互換性を確保するための重要なステップです。

src/pkg/syscall/sockcmsg_unix.goの変更

このファイルは、Unix系OSにおけるソケットコントロールメッセージ(Cmsg)の処理に関連する関数を含んでいます。特に注目すべきはcmsgData関数の変更です。

cmsgData関数は、Cmsghdr構造体のポインタhを受け取り、そのヘッダの直後に続くコントロールデータの開始アドレスを計算して返します。

変更前のコード:

func cmsgData(h *Cmsghdr) unsafe.Pointer {
	return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + SizeofCmsghdr)
}

このコードでは、Cmsghdrのポインタhuintptrに変換し、それにCmsghdrのサイズ(SizeofCmsghdr)を単純に加算していました。これは、ヘッダの直後にデータが連続して配置されているという前提に基づいています。しかし、前述の通り、一部のOSではCmsghdrとデータの間に追加のパディングが必要となる場合があります。

変更後のコード:

func cmsgData(h *Cmsghdr) unsafe.Pointer {
	return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(SizeofCmsghdr)))
}

この変更では、SizeofCmsghdrを直接加算する代わりに、cmsgAlignOfという関数を介して計算されたオフセットを使用しています。cmsgAlignOf関数は、引数として与えられたサイズ(ここではSizeofCmsghdr)を、OSが要求する適切なアライメント境界に切り上げる役割を担います。例えば、もしOSが8バイトアライメントを要求し、SizeofCmsghdrが28バイトだった場合、cmsgAlignOf(28)は32を返すかもしれません。これにより、Cmsghdrの直後のデータが常に正しくアライメントされたアドレスから始まることが保証されます。

この修正により、FreeBSDやNetBSDといったOS固有のアライメント要件が満たされ、ソケット経由でのファイルディスクリプタの受け渡しが安定して行われるようになります。

関連リンク

参考にした情報源リンク

このコミットは、Go言語のsyscallパッケージにおけるファイルディスクリプタ(FD)の受け渡しに関するバグ修正です。特にFreeBSDとNetBSDというUnix系OS上での挙動に焦点を当て、コントロールメッセージ(Cmsg)のデータ部分へのポインタ計算におけるアライメントの問題を解決しています。これにより、これらのOSでソケット経由でのFD受け渡しが正しく機能するようになります。

コミット

commit 98d44d140d7abde9fdfbdbf7adec5be7bb0892ce
Author: Dave Cheney <dave@cheney.net>
Date:   Wed Feb 27 09:13:15 2013 +1100

    syscall: fix FD passing on FreeBSD and NetBSD
    
    Fixes #3348.
    
    R=devon.odell, minux.ma, bradfitz, mdempsky
    CC=golang-dev
    https://golang.org/cl/7406050

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

https://github.com/golang/go/commit/98d44d140d7abde9fdfbdbf7adec5be7bb0892ce

元コミット内容

syscall: fix FD passing on FreeBSD and NetBSD

Fixes #3348.

R=devon.odell, minux.ma, bradfitz, mdempsky
CC=golang-dev
https://golang.org/cl/7406050

変更の背景

Go言語のsyscallパッケージは、OSのシステムコールをGoプログラムから呼び出すためのインターフェースを提供します。ファイルディスクリプタ(FD)の受け渡しは、プロセス間通信(IPC)において、あるプロセスがオープンしているファイルやソケットのFDを別のプロセスに安全に渡すための重要な機能です。これは通常、Unixドメインソケットを介してコントロールメッセージ(SCM_RIGHTS)として行われます。

Goの初期バージョンでは、FreeBSDやNetBSDといった一部のUnix系OSにおいて、このFD受け渡し機能が正しく動作しないというバグ(Issue #3348)が報告されていました。この問題は、コントロールメッセージの構造体(Cmsghdr)の後に続くデータ部分のアライメント(メモリ配置の制約)が、OSによって異なるために発生していました。特に、FreeBSDやNetBSDでは、データ部分が特定のバイト境界にアライメントされている必要がありましたが、Goの既存の実装ではこのアライメントが考慮されていなかったため、不正なメモリアクセスやデータの破損が発生し、FDの受け渡しが失敗していました。

このコミットは、このFD受け渡しに関するバグを修正し、GoプログラムがFreeBSDおよびNetBSD上でも安定してFDをやり取りできるようにすることを目的としています。

前提知識の解説

1. ファイルディスクリプタ(File Descriptor, FD)

ファイルディスクリプタは、Unix系OSにおいて、プロセスがファイル、ソケット、パイプなどのI/Oリソースを識別するために使用する整数値です。プロセスはFDを通じてこれらのリソースにアクセスします。

2. プロセス間通信(Inter-Process Communication, IPC)

異なるプロセス間でデータや情報を交換するメカニズムの総称です。FDの受け渡しは、IPCの一種であり、特に特権を伴うリソースの共有に利用されます。

3. Unixドメインソケット

同じホスト上のプロセス間での通信に特化したソケットの一種です。ネットワークソケットとは異なり、ファイルシステム上のパス名で識別されます。高速で安全なIPC手段として広く利用されます。

4. コントロールメッセージ(Control Message)

ソケット通信において、通常のデータとは別に、補助的な情報(例: ファイルディスクリプタ、認証情報、帯域外データ)をやり取りするためのメカニズムです。sendmsg()recvmsg()システムコールで送受信されます。

5. Cmsghdr構造体

コントロールメッセージのヘッダ部分を定義する構造体です。メッセージのタイプ(例: SCM_RIGHTS)、長さ、レベルなどの情報を含みます。このヘッダの直後に、実際のコントロールデータ(例: 渡されるFDのリスト)が続きます。

6. SCM_RIGHTS

ファイルディスクリプタをコントロールメッセージとして送受信する際に使用されるコントロールメッセージタイプです。

7. メモリアライメント(Memory Alignment)

コンピュータのメモリ上でデータが配置される際の、特定のバイト境界への制約です。CPUは、特定のデータ型(例: 整数、ポインタ)が特定のバイト境界(例: 4バイト、8バイト)に配置されている場合に、より効率的にアクセスできます。アライメントが正しくないと、パフォーマンスの低下や、最悪の場合、不正なメモリアクセスによるプログラムのクラッシュを引き起こす可能性があります。特に、異なるアーキテクチャやOSでは、アライメントの要件が異なることがあります。

8. unsafeパッケージ(Go言語)

Go言語のunsafeパッケージは、型安全性をバイパスして、ポインタ演算や型変換を可能にする機能を提供します。これは、C言語との相互運用や、低レベルのシステムプログラミング(syscallパッケージなど)で必要となることがありますが、誤用するとメモリ破壊や未定義動作を引き起こす可能性があるため、慎重に使用する必要があります。

  • unsafe.Pointer: 任意の型のポインタを保持できる汎用ポインタ。
  • uintptr: ポインタを整数として扱うことができる型。ポインタ演算を行う際に使用されます。

技術的詳細

このコミットの核心は、Cmsghdr構造体の直後に続くコントロールデータのアドレス計算にあります。Unix系OSでは、Cmsghdrのサイズは固定ですが、その後に続くデータ部分が特定のバイト境界にアライメントされている必要があります。このアライメント要件はOSによって異なり、単にCmsghdrのサイズを足すだけでは不十分な場合があります。

元のコードでは、cmsgData関数がコントロールデータの開始アドレスを計算する際に、Cmsghdrのサイズ(SizeofCmsghdr)を単純に加算していました。

return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + SizeofCmsghdr)

しかし、FreeBSDやNetBSDのようなシステムでは、Cmsghdrの直後のデータが、そのシステムのポインタサイズやデータ型の最大アライメント要件に合わせてパディングされる必要があります。このパディングを考慮しないと、データ部分の開始アドレスが不正なアライメントになり、FDの受け渡しが失敗する原因となります。

修正後のコードでは、cmsgAlignOfという新しい関数が導入され、SizeofCmsghdrをこのアライメント関数に通すことで、OSが要求する適切なアライメントを考慮したオフセットを計算しています。

return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(SizeofCmsghdr)))

cmsgAlignOf関数は、おそらくOS固有のアライメント要件(例えば、CMSG_ALIGNマクロに相当するロジック)をカプセル化しており、与えられたサイズを次の適切なアライメント境界に切り上げる役割を果たします。これにより、Cmsghdrの直後のデータが常に正しくアライメントされたアドレスから始まることが保証され、FDの受け渡しが安定して行われるようになります。

また、passfd_test.goのビルドタグにfreebsdnetbsdが追加されたことは、この修正がこれらのOSでテストされることを意味しており、修正の有効性を確認するための重要なステップです。

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

このコミットによる主要なコード変更は以下の2ファイルです。

  1. src/pkg/syscall/passfd_test.go
  2. src/pkg/syscall/sockcmsg_unix.go

src/pkg/syscall/passfd_test.go

--- a/src/pkg/syscall/passfd_test.go
+++ b/src/pkg/syscall/passfd_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build linux darwin
+// +build linux darwin freebsd netbsd
 
 package syscall_test

src/pkg/syscall/sockcmsg_unix.go

--- a/src/pkg/syscall/sockcmsg_unix.go
+++ b/src/pkg/syscall/sockcmsg_unix.go
@@ -37,7 +37,7 @@ func CmsgSpace(datalen int) int {
 }
 
 func cmsgData(h *Cmsghdr) unsafe.Pointer {
-	return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + SizeofCmsghdr)
+	return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(SizeofCmsghdr)))
 }
 
 // SocketControlMessage represents a socket control message.

コアとなるコードの解説

src/pkg/syscall/passfd_test.goの変更

このファイルは、ファイルディスクリプタの受け渡し機能をテストするためのコードを含んでいます。変更前はlinuxdarwin(macOS)でのみビルドされるように指定されていました。

// +build linux darwin

この行が以下のように変更されました。

// +build linux darwin freebsd netbsd

これは、passfd_test.goに含まれるテストコードが、今後はFreeBSDとNetBSD上でもコンパイルされ、実行されるべきであることを示しています。これにより、今回のFD受け渡しに関する修正が、これらのOSで正しく機能するかどうかを自動的に検証できるようになります。これは、クロスプラットフォームな互換性を確保するための重要なステップです。

src/pkg/syscall/sockcmsg_unix.goの変更

このファイルは、Unix系OSにおけるソケットコントロールメッセージ(Cmsg)の処理に関連する関数を含んでいます。特に注目すべきはcmsgData関数の変更です。

cmsgData関数は、Cmsghdr構造体のポインタhを受け取り、そのヘッダの直後に続くコントロールデータの開始アドレスを計算して返します。

変更前のコード:

func cmsgData(h *Cmsghdr) unsafe.Pointer {
	return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + SizeofCmsghdr)
}

このコードでは、Cmsghdrのポインタhuintptrに変換し、それにCmsghdrのサイズ(SizeofCmsghdr)を単純に加算していました。これは、ヘッダの直後にデータが連続して配置されているという前提に基づいています。しかし、前述の通り、一部のOSではCmsghdrとデータの間に追加のパディングが必要となる場合があります。

変更後のコード:

func cmsgData(h *Cmsghdr) unsafe.Pointer {
	return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(SizeofCmsghdr)))
}

この変更では、SizeofCmsghdrを直接加算する代わりに、cmsgAlignOfという関数を介して計算されたオフセットを使用しています。cmsgAlignOf関数は、引数として与えられたサイズ(ここではSizeofCmsghdr)を、OSが要求する適切なアライメント境界に切り上げる役割を担います。例えば、もしOSが8バイトアライメントを要求し、SizeofCmsghdrが28バイトだった場合、cmsgAlignOf(28)は32を返すかもしれません。これにより、Cmsghdrの直後のデータが常に正しくアライメントされたアドレスから始まることが保証されます。

この修正により、FreeBSDやNetBSDといったOS固有のアライメント要件が満たされ、ソケット経由でのファイルディスクリプタの受け渡しが安定して行われるようになります。

関連リンク

参考にした情報源リンク