[インデックス 18684] ファイルの概要
コミット
commit 0399b971d9ea0e061fb1361b448f9a8188ee2336
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Thu Feb 27 14:16:47 2014 -0800
syscall: add some paranoia in Linux's Accept4
Fixes #7428
LGTM=r
R=r
CC=golang-codereviews
https://golang.org/cl/69530044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0399b971d9ea0e061fb1361b448f9a8188ee2336
元コミット内容
syscall: add some paranoia in Linux's Accept4
Fixes #7428
LGTM=r
R=r
CC=golang-codereviews
https://golang.org/cl/69530044
変更の背景
このコミットは、Go言語のsyscall
パッケージにおけるLinuxのAccept4
システムコール実装に、"paranoia"(偏執的なまでの用心深さ)を追加するものです。具体的には、Accept4
システムコールが返すソケットアドレスのサイズに関する潜在的な問題を修正することを目的としています。コミットメッセージに「Fixes #7428」とあることから、GoのIssueトラッカーで報告されたバグや脆弱性に対応するための変更であることが示唆されます。
Accept4
システムコールは、新しい接続を受け入れる際にクライアントのソケットアドレス情報を取得しますが、この情報が格納されるバッファのサイズが適切でない場合に、メモリ破壊や情報漏洩といったセキュリティ上の問題を引き起こす可能性があります。このコミットは、そのようなリスクを軽減するための防御的なチェックを追加しています。
前提知識の解説
1. システムコール (System Call)
システムコールは、オペレーティングシステム (OS) のカーネルが提供するサービスを、ユーザー空間のプログラムが利用するためのインターフェースです。ファイル操作、ネットワーク通信、メモリ管理など、OSの機能にアクセスする際に使用されます。Go言語のsyscall
パッケージは、これらのシステムコールをGoプログラムから直接呼び出すための機能を提供します。
2. Accept4
システムコール
Accept4
はLinuxカーネルが提供するシステムコールの一つで、ソケットに対する新しい接続を受け入れます。従来のaccept
システムコールと異なり、Accept4
は追加のフラグ引数を受け取ることができ、これによりソケットの振る舞いをより細かく制御できます(例: SOCK_NONBLOCK
で非ブロッキングモードに設定、SOCK_CLOEXEC
でexec
時にクローズ)。
Accept4
の基本的なシグネチャは以下のようになります(C言語風の表現):
int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
sockfd
: 接続を受け入れるリスニングソケットのファイルディスクリプタ。addr
: クライアントのソケットアドレス情報が格納されるバッファへのポインタ。addrlen
:addr
バッファのサイズを指すポインタ。呼び出し時にはバッファの最大サイズを渡し、戻り時には実際に書き込まれたソケットアドレスのサイズが格納されます。flags
:SOCK_NONBLOCK
やSOCK_CLOEXEC
などのフラグ。
3. Sockaddr
と RawSockaddrAny
ネットワークプログラミングにおいて、ソケットアドレスは接続元や接続先の情報を表現するために使用されます。これにはIPアドレスとポート番号が含まれます。C言語では、これらのアドレスはstruct sockaddr
という汎用的な構造体、またはstruct sockaddr_in
(IPv4用)、struct sockaddr_in6
(IPv6用)といった具体的な構造体で表現されます。
Go言語のsyscall
パッケージでは、これらのC言語の構造体に対応する型が定義されています。
Sockaddr
は、Go言語におけるソケットアドレスのインターフェースまたは抽象型です。具体的なアドレスタイプ(IPv4, IPv6, Unixドメインなど)に応じて、SockaddrInet4
,SockaddrInet6
,SockaddrUnix
などの具体的な型がこのインターフェースを実装します。RawSockaddrAny
は、任意のソケットアドレスを格納できる十分な大きさを持つ生(raw)のバイト配列と、そのアドレスのファミリー(sa_family
)を保持する構造体です。これは、Accept4
のようなシステムコールが、どのような種類のアドレスが返されるか事前に分からない場合に、汎用的なバッファとして使用されます。RawSockaddrAny
のサイズは、サポートされる最大のアドレス構造体(通常はIPv6アドレス構造体)を格納できるだけの大きさが必要です。
4. SizeofSockaddrAny
SizeofSockaddrAny
は、RawSockaddrAny
構造体のサイズ(バイト単位)を表す定数です。これは、RawSockaddrAny
が格納できるソケットアドレスの最大サイズを決定します。
技術的詳細
このコミットの核心は、LinuxのAccept4
システムコールをGo言語でラップする際に、ソケットアドレスのバッファサイズに関する「偏執的なまでの用心深さ」を追加することです。
Goのsyscall.Accept4
関数は、内部でRawSockaddrAny
型のバッファをソケットアドレスの格納に使用します。Accept4
システムコールは、addrlen
引数を通じて、実際に書き込まれたソケットアドレスのサイズを返します。この返されたサイズが、Goが用意したRawSockaddrAny
バッファのサイズ(SizeofSockaddrAny
)よりも大きい場合、これは深刻な問題を示唆します。
通常、Accept4
システムコールは、addrlen
に渡された値(Go側ではSizeofSockaddrAny
)を超えてデータを書き込むことはありません。しかし、もし何らかの理由(例えば、カーネルのバグ、あるいは将来的に非常に大きなソケットアドレス構造体が導入された場合など)で、システムコールがRawSockaddrAny
のサイズを超えるデータを書き込もうとした場合、これはバッファオーバーフローを引き起こし、Goプログラムのメモリを破壊する可能性があります。
このコミットで追加されたチェックは、まさにこの異常な状況を検出するためのものです。
if len > SizeofSockaddrAny {
panic("RawSockaddrAny too small")
}
ここで、len
はAccept4
システムコールが実際に書き込んだソケットアドレスのサイズです。このlen
がGoが想定するRawSockaddrAny
の最大サイズ(SizeofSockaddrAny
)を超えている場合、それは予期せぬ事態であり、プログラムの整合性を保つためにpanic
を引き起こします。
このpanic
は、通常発生しないはずの異常な状態を早期に検出し、プログラムが不正な状態で続行するのを防ぐための防御的なメカニズムです。これにより、潜在的なセキュリティ脆弱性や未定義の動作を防ぐことができます。
コアとなるコードの変更箇所
変更はsrc/pkg/syscall/syscall_linux.go
ファイルにあります。
--- a/src/pkg/syscall/syscall_linux.go
+++ b/src/pkg/syscall/syscall_linux.go
@@ -420,6 +420,9 @@ func Accept4(fd int, flags int) (nfd int, sa Sockaddr, err error) {
if err != nil {
return
}
+ if len > SizeofSockaddrAny {
+ panic("RawSockaddrAny too small")
+ }
sa, err = anyToSockaddr(&rsa)
if err != nil {
Close(nfd)
具体的には、Accept4
関数の内部で、システムコールからの戻り値であるlen
(実際に受け取ったソケットアドレスのサイズ)が、RawSockaddrAny
の最大サイズであるSizeofSockaddrAny
を超えていないかをチェックする行が追加されています。
コアとなるコードの解説
追加された3行のコードは、Accept4
システムコールが成功した直後に実行されます。
if len > SizeofSockaddrAny {
panic("RawSockaddrAny too small")
}
len
: これはAccept4
システムコールが、クライアントのソケットアドレス情報を格納したバッファの実際のサイズとして返した値です。この値は、Accept4
のaddrlen
引数に対応します。SizeofSockaddrAny
: これはGoのsyscall
パッケージ内で定義されている定数で、RawSockaddrAny
構造体のバイトサイズを表します。RawSockaddrAny
は、Goがソケットアドレスを受け取るために内部的に使用する汎用バッファです。panic("RawSockaddrAny too small")
: もしlen
がSizeofSockaddrAny
よりも大きい場合、これはGoが用意したバッファが、システムコールが書き込もうとしたデータに対して小さすぎることを意味します。これは通常発生しない異常な状態であり、メモリ破壊やセキュリティ上の問題につながる可能性があるため、プログラムの実行を即座に停止(パニック)させます。このパニックは、開発者に対して、Goのsyscall
パッケージの内部実装、またはOSカーネルの動作に予期せぬ問題があることを警告します。
このチェックは、Goのsyscall
パッケージが、OSとのインターフェースにおいて堅牢性と安全性を確保しようとする姿勢を示しています。
関連リンク
- Go Issue #7428: https://github.com/golang/go/issues/7428 (このコミットが修正したとされるIssue)
- Go CL 69530044: https://golang.org/cl/69530044 (このコミットに対応するGoのコードレビューリンク)
accept4
man page: https://man7.org/linux/man-pages/man2/accept4.2.html (Linuxのaccept4
システムコールに関する公式ドキュメント)
参考にした情報源リンク
- 上記の関連リンク
- Go言語の
syscall
パッケージのドキュメント (Goの公式ドキュメントサイト) - Linuxのシステムコールに関する一般的な知識
- ネットワークプログラミングにおけるソケットアドレスの概念
- バッファオーバーフローとセキュリティに関する一般的な知識I have provided the detailed explanation as requested.