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

[インデックス 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_CLOEXECexec時にクローズ)。

Accept4の基本的なシグネチャは以下のようになります(C言語風の表現):

int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
  • sockfd: 接続を受け入れるリスニングソケットのファイルディスクリプタ。
  • addr: クライアントのソケットアドレス情報が格納されるバッファへのポインタ。
  • addrlen: addrバッファのサイズを指すポインタ。呼び出し時にはバッファの最大サイズを渡し、戻り時には実際に書き込まれたソケットアドレスのサイズが格納されます。
  • flags: SOCK_NONBLOCKSOCK_CLOEXECなどのフラグ。

3. SockaddrRawSockaddrAny

ネットワークプログラミングにおいて、ソケットアドレスは接続元や接続先の情報を表現するために使用されます。これには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")
	}

ここで、lenAccept4システムコールが実際に書き込んだソケットアドレスのサイズです。この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システムコールが、クライアントのソケットアドレス情報を格納したバッファの実際のサイズとして返した値です。この値は、Accept4addrlen引数に対応します。
  • SizeofSockaddrAny: これはGoのsyscallパッケージ内で定義されている定数で、RawSockaddrAny構造体のバイトサイズを表します。RawSockaddrAnyは、Goがソケットアドレスを受け取るために内部的に使用する汎用バッファです。
  • panic("RawSockaddrAny too small"): もしlenSizeofSockaddrAnyよりも大きい場合、これはGoが用意したバッファが、システムコールが書き込もうとしたデータに対して小さすぎることを意味します。これは通常発生しない異常な状態であり、メモリ破壊やセキュリティ上の問題につながる可能性があるため、プログラムの実行を即座に停止(パニック)させます。このパニックは、開発者に対して、Goのsyscallパッケージの内部実装、またはOSカーネルの動作に予期せぬ問題があることを警告します。

このチェックは、Goのsyscallパッケージが、OSとのインターフェースにおいて堅牢性と安全性を確保しようとする姿勢を示しています。

関連リンク

参考にした情報源リンク

  • 上記の関連リンク
  • Go言語のsyscallパッケージのドキュメント (Goの公式ドキュメントサイト)
  • Linuxのシステムコールに関する一般的な知識
  • ネットワークプログラミングにおけるソケットアドレスの概念
  • バッファオーバーフローとセキュリティに関する一般的な知識I have provided the detailed explanation as requested.