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

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

このコミットは、Darwin(macOS)カーネルにおける accept() システムコールの既知のバグに対するワークアラウンドをGo言語の syscall パッケージに導入するものです。このバグにより、本来 ECONNABORTED エラーを返すはずの状況で、アドレス情報を持たないソケットが誤って受け入れられてしまい、結果としてGoのHTTPサーバーがポートスキャンなどの単純な操作によってサービス拒否(DoS)攻撃を受けやすくなる問題が解決されます。

コミット

commit 5197fa80405a470eef7cd2540380172aea6d60af
Author: Alexey Borzenkov <snaury@gmail.com>
Date:   Mon Jul 30 09:02:24 2012 +1000

    syscall: workaround accept() bug on Darwin
    
    Darwin kernels have a bug in accept() where error result from
    an internal call is not checked and socket is accepted instead
    of ECONNABORTED error. However, such sockets have no sockaddr,
    which results in EAFNOSUPPORT error from anyToSockaddr, making
    Go http servers running on Mac OS X easily susceptible to
    denial of service from simple port scans with nmap.
    Fixes #3849.
    
    R=golang-dev, adg, mikioh.mikioh
    CC=golang-dev
    https://golang.org/cl/6456045

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

https://github.com/golang/go/commit/5197fa80405a470eef7cd2540380172aea6d60af

元コミット内容

Darwinカーネルの accept() にはバグがあり、内部呼び出しからのエラー結果がチェックされず、ECONNABORTED エラーの代わりにソケットが受け入れられてしまう。しかし、そのようなソケットは sockaddr を持たないため、anyToSockaddr から EAFNOSUPPORT エラーが発生する。これにより、Mac OS X上で動作するGoのHTTPサーバーは、nmapによる単純なポートスキャンによって容易にサービス拒否攻撃を受けやすくなる。このコミットは #3849 を修正する。

変更の背景

この変更は、Go言語で書かれたHTTPサーバーがmacOS(旧称:OS X)上で動作する際に直面していた深刻なサービス拒否(DoS)脆弱性に対処するために行われました。問題の根源は、Darwinカーネル(macOSの基盤となるXNUカーネル)における accept() システムコールの特定のバグにありました。

通常、TCP接続の確立中にクライアント側が接続を中断した場合(例えば、SYN-ACKパケットを受信した後にRSTを送信するなど)、サーバー側の accept() 呼び出しは ECONNABORTED エラーを返すべきです。これは、接続が正常に確立されなかったことを示す標準的な挙動です。しかし、Darwinカーネルのバグにより、このエラーが適切に処理されず、代わりに「アドレス情報を持たないソケット」が accept() によって返されてしまうことがありました。

Goのネットワークスタックでは、accept() が返したソケットディスクリプタと関連するアドレス情報(sockaddr)を anyToSockaddr 関数でGoの Sockaddr 型に変換しようとします。しかし、バグによって返されたソケットには有効な sockaddr が関連付けられていないため、anyToSockaddrEAFNOSUPPORT(Address family not supported by protocol)エラーを返してしまいます。

この一連の挙動は、特にnmapのようなポートスキャンツールが使用された場合に問題となります。nmapは、ターゲットホストのポートの状態を効率的に調査するために、多数の半開接続(SYNスキャンなど)を試みることがあります。この際、接続が途中で中断されると、Darwinのバグがトリガーされ、GoのHTTPサーバーは無効なソケットを受け入れ、その処理中にエラーを発生させます。このエラーが頻繁に発生すると、サーバーは正当な接続を処理できなくなり、結果としてサービス拒否状態に陥る可能性がありました。

このコミットは、このカーネルバグによって引き起こされるGoアプリケーションの脆弱性を緩和するための直接的なワークアラウンドとして導入されました。

前提知識の解説

  • accept() システムコール: サーバープログラムがクライアントからの新しい接続要求を受け入れるために使用するシステムコールです。通常、accept() は新しいソケットディスクリプタと、接続してきたクライアントのアドレス情報(sockaddr 構造体)を返します。
  • sockaddr: ソケットアドレス情報を格納するための汎用的な構造体です。IPアドレスやポート番号など、ネットワーク接続のエンドポイントを識別するために使用されます。異なるプロトコル(IPv4, IPv6など)に対応するために、sockaddr_insockaddr_in6 などの具体的な構造体が sockaddr にキャストされて使用されます。
  • ECONNABORTED: エラーコードの一つで、接続が中断されたことを示します。通常、サーバーが accept() を呼び出す前にクライアントが接続をリセットした場合などに発生します。
  • EAFNOSUPPORT: エラーコードの一つで、「アドレスファミリーがプロトコルでサポートされていない」ことを意味します。これは、指定されたアドレスファミリー(例: IPv4, IPv6)が、使用しようとしているソケットタイプやプロトコルで無効である場合に発生します。この文脈では、accept() が返したソケットに有効なアドレス情報が関連付けられていないため、Goの anyToSockaddr 関数がそのソケットのアドレスファミリーを認識できず、このエラーを返していました。
  • Darwinカーネル / XNUカーネル: AppleのmacOS、iOS、watchOS、tvOSの基盤となっているオペレーティングシステムカーネルです。XNUは "X is Not Unix" の略で、MachカーネルとFreeBSDのコンポーネントを組み合わせたハイブリッドカーネルです。
  • nmap: ネットワーク探索とセキュリティ監査のためのオープンソースツールです。ポートスキャン機能は、ターゲットホスト上で開いているポートや実行されているサービスを特定するために広く使用されます。nmapのSYNスキャン(-sS オプション)は、完全なTCP接続を確立せずにポートの状態を調査するため、半開接続を多数生成し、これが今回のバグをトリガーする一因となりました。
  • サービス拒否 (DoS): 正当なユーザーがサービスを利用できないようにするサイバー攻撃の一種です。このケースでは、サーバーが不正なソケット処理にリソースを消費し、正当な接続を処理できなくなることでDoS状態に陥ります。

技術的詳細

Darwinカーネルの accept() バグは、TCP接続の確立プロセスにおける特定の競合状態または内部エラー処理の不備に起因していました。具体的には、クライアントが接続を中断した際に、カーネルが ECONNABORTED エラーを適切に accept() の呼び出し元に伝播させる代わりに、不完全なソケット構造体を返してしまうというものでした。

この「不完全なソケット」とは、ソケットディスクリプタ自体は有効であるものの、そのソケットに関連付けられるべき sockaddr 構造体(リモートピアのアドレス情報を含む)が空であるか、または無効な状態であることを意味します。

Go言語の syscall パッケージ内の Accept 関数は、内部的にC言語の accept システムコールを呼び出した後、返されたソケットディスクリプタとアドレス情報を処理します。この処理の一部として、anyToSockaddr 関数が呼び出され、生の sockaddr データをGoの Sockaddr インターフェース型に変換しようとします。

バグのある accept() の挙動により、anyToSockaddr に渡される sockaddr データが空(len == 0)であるか、または解析不能な状態であったため、anyToSockaddr はそのアドレスファミリーを特定できず、結果として EAFNOSUPPORT エラーを返していました。

この問題は、GoのHTTPサーバーが accept() から返されたソケットを処理する際に、予期せぬ EAFNOSUPPORT エラーに遭遇し、そのソケットを破棄する、またはエラー処理ロジックがトリガーされることで、サーバーのパフォーマンス低下やリソース枯渇を引き起こし、最終的に正当な接続の受け入れを妨げるサービス拒否状態につながっていました。

このコミットは、accept() から返されたソケットのアドレス情報の長さ(len)がゼロであるという、このバグに特有の症状を検出し、それを ECONNABORTED エラーとして扱うことで、Goアプリケーションがこのカーネルバグの影響を回避できるようにします。

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

変更は src/pkg/syscall/syscall_bsd.go ファイルの Accept 関数内で行われました。

--- a/src/pkg/syscall/syscall_bsd.go
+++ b/src/pkg/syscall/syscall_bsd.go
@@ -303,6 +303,14 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) {
 	if err != nil {
 		return
 	}\n+\tif len == 0 {\n+\t\t// Accepted socket has no address.\n+\t\t// This is likely due to a bug in xnu kernels,\n+\t\t// where instead of ECONNABORTED error socket\n+\t\t// is accepted, but has no address.\n+\t\tClose(nfd)\n+\t\treturn 0, nil, ECONNABORTED\n+\t}\n 	sa, err = anyToSockaddr(&rsa)\n 	if err != nil {\n 		Close(nfd)\

具体的には、Accept 関数内で accept4 (または accept) システムコールが呼び出され、その結果として新しいファイルディスクリプタ nfd とアドレス情報の長さ len が取得された直後に、以下の8行が追加されました。

コアとなるコードの解説

追加されたコードブロックは以下の通りです。

	if len == 0 {
		// Accepted socket has no address.
		// This is likely due to a bug in xnu kernels,
		// where instead of ECONNABORTED error socket
		// is accepted, but has no address.
		Close(nfd)
		return 0, nil, ECONNABORTED
	}

このコードは、accept() システムコールが成功したと報告したにもかかわらず、返されたソケットに関連付けられたアドレス情報(sockaddr)の長さが 0 であるという、特定の異常な状態をチェックしています。

  1. if len == 0: これは、accept() が新しい接続を受け入れたと報告したが、その接続のリモートピアのアドレス情報(sockaddr)のサイズがゼロである、という異常なケースを検出します。コミットメッセージにあるように、これはDarwinカーネルのバグによって引き起こされる既知の症状です。本来 ECONNABORTED エラーが返されるべき状況で、アドレス情報を持たないソケットが誤って受け入れられてしまうのです。
  2. コメント: 追加されたコメントは、この len == 0 の状態がXNUカーネルのバグによるものであることを明確に説明しています。このバグは、ECONNABORTED エラーの代わりにソケットが受け入れられるが、そのソケットにはアドレスがない、という挙動を示します。
  3. Close(nfd): 誤って受け入れられた(しかし無効な)ソケットディスクリプタ nfd を閉じます。これにより、システムリソースのリークを防ぎ、この無効なソケットが後続の処理で問題を引き起こすのを防ぎます。
  4. return 0, nil, ECONNABORTED: この関数呼び出し(Accept)を、新しいファイルディスクリプタ 0(無効な値)、nilSockaddr、そして ECONNABORTED エラーを返して終了します。これにより、Goのネットワークスタックのより上位の層では、この接続がクライアントによって中断されたものとして適切に処理されるようになります。これは、本来カーネルが返すはずだったエラーをGo側でエミュレートすることで、アプリケーションが予期せぬ EAFNOSUPPORT エラーに遭遇するのを防ぎ、より堅牢なエラーハンドリングを可能にします。

この変更により、GoのHTTPサーバーは、nmapのようなツールによるポートスキャンによって引き起こされる無効な接続試行を適切に処理し、サービス拒否状態に陥ることを回避できるようになりました。

関連リンク

参考にした情報源リンク

  • https://github.com/golang/go/commit/5197fa80405a470eef7cd2540380172aea6d60af
  • accept() system call documentation (general Unix/Linux man pages)
  • sockaddr structure documentation (general Unix/Linux man pages)
  • ECONNABORTED and EAFNOSUPPORT error codes (general Unix/Linux errno man pages)
  • XNU kernel architecture overview (Apple Developer Documentation, general knowledge)
  • nmap documentation (general knowledge of port scanning)
  • Go syscall package source code (contextual understanding)
  • Discussion around Go issue #3849 (if publicly available, for deeper context on the bug's impact and diagnosis)
    • (Note: The original issue link points to Google Code, which has been migrated. Searching for "Go issue 3849" on GitHub might lead to the migrated issue or related discussions.)

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

このコミットは、Darwin(macOS)カーネルにおける accept() システムコールの既知のバグに対するワークアラウンドをGo言語の syscall パッケージに導入するものです。このバグにより、本来 ECONNABORTED エラーを返すはずの状況で、アドレス情報を持たないソケットが誤って受け入れられてしまい、結果としてGoのHTTPサーバーがポートスキャンなどの単純な操作によってサービス拒否(DoS)攻撃を受けやすくなる問題が解決されます。

コミット

commit 5197fa80405a470eef7cd2540380172aea6d60af
Author: Alexey Borzenkov <snaury@gmail.com>
Date:   Mon Jul 30 09:02:24 2012 +1000

    syscall: workaround accept() bug on Darwin
    
    Darwin kernels have a bug in accept() where error result from
    an internal call is not checked and socket is accepted instead
    of ECONNABORTED error. However, such sockets have no sockaddr,
    which results in EAFNOSUPPORT error from anyToSockaddr, making
    Go http servers running on Mac OS X easily susceptible to
    denial of service from simple port scans with nmap.
    Fixes #3849.
    
    R=golang-dev, adg, mikioh.mikioh
    CC=golang-dev
    https://golang.org/cl/6456045

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

https://github.com/golang/go/commit/5197fa80405a470eef7cd2540380172aea6d60af

元コミット内容

Darwinカーネルの accept() にはバグがあり、内部呼び出しからのエラー結果がチェックされず、ECONNABORTED エラーの代わりにソケットが受け入れられてしまう。しかし、そのようなソケットは sockaddr を持たないため、anyToSockaddr から EAFNOSUPPORT エラーが発生する。これにより、Mac OS X上で動作するGoのHTTPサーバーは、nmapによる単純なポートスキャンによって容易にサービス拒否攻撃を受けやすくなる。このコミットは #3849 を修正する。

変更の背景

この変更は、Go言語で書かれたHTTPサーバーがmacOS(旧称:OS X)上で動作する際に直面していた深刻なサービス拒否(DoS)脆弱性に対処するために行われました。問題の根源は、Darwinカーネル(macOSの基盤となるXNUカーネル)における accept() システムコールの特定のバグにありました。

通常、TCP接続の確立中にクライアント側が接続を中断した場合(例えば、SYN-ACKパケットを受信した後にRSTを送信するなど)、サーバー側の accept() 呼び出しは ECONNABORTED エラーを返すべきです。これは、接続が正常に確立されなかったことを示す標準的な挙動です。しかし、Darwinカーネルのバグにより、このエラーが適切に処理されず、代わりに「アドレス情報を持たないソケット」が accept() によって返されてしまうことがありました。

Goのネットワークスタックでは、accept() が返したソケットディスクリプタと関連するアドレス情報(sockaddr)を anyToSockaddr 関数でGoの Sockaddr 型に変換しようとします。しかし、バグによって返されたソケットには有効な sockaddr が関連付けられていないため、anyToSockaddrEAFNOSUPPORT(Address family not supported by protocol)エラーを返してしまいます。

この一連の挙動は、特にnmapのようなポートスキャンツールが使用された場合に問題となります。nmapは、ターゲットホストのポートの状態を効率的に調査するために、多数の半開接続(SYNスキャンなど)を試みることがあります。この際、接続が途中で中断されると、Darwinのバグがトリガーされ、GoのHTTPサーバーは無効なソケットを受け入れ、その処理中にエラーを発生させます。このエラーが頻繁に発生すると、サーバーは正当な接続を処理できなくなり、結果としてサービス拒否状態に陥る可能性がありました。

このコミットは、このカーネルバグによって引き起こされるGoアプリケーションの脆弱性を緩和するための直接的なワークアラウンドとして導入されました。

前提知識の解説

  • accept() システムコール: サーバープログラムがクライアントからの新しい接続要求を受け入れるために使用するシステムコールです。通常、accept() は新しいソケットディスクリプタと、接続してきたクライアントのアドレス情報(sockaddr 構造体)を返します。
  • sockaddr: ソケットアドレス情報を格納するための汎用的な構造体です。IPアドレスやポート番号など、ネットワーク接続のエンドポイントを識別するために使用されます。異なるプロトコル(IPv4, IPv6など)に対応するために、sockaddr_insockaddr_in6 などの具体的な構造体が sockaddr にキャストされて使用されます。
  • ECONNABORTED: エラーコードの一つで、接続が中断されたことを示します。通常、サーバーが accept() を呼び出す前にクライアントが接続をリセットした場合などに発生します。
  • EAFNOSUPPORT: エラーコードの一つで、「アドレスファミリーがプロトコルでサポートされていない」ことを意味します。これは、指定されたアドレスファミリー(例: IPv4, IPv6)が、使用しようとしているソケットタイプやプロトコルで無効である場合に発生します。この文脈では、accept() が返したソケットに有効なアドレス情報が関連付けられていないため、Goの anyToSockaddr 関数がそのソケットのアドレスファミリーを認識できず、このエラーを返していました。
  • Darwinカーネル / XNUカーネル: AppleのmacOS、iOS、watchOS、tvOSの基盤となっているオペレーティングシステムカーネルです。XNUは "X is Not Unix" の略で、MachカーネルとFreeBSDのコンポーネントを組み合わせたハイブリッドカーネルです。
  • nmap: ネットワーク探索とセキュリティ監査のためのオープンソースツールです。ポートスキャン機能は、ターゲットホスト上で開いているポートや実行されているサービスを特定するために広く使用されます。nmapのSYNスキャン(-sS オプション)は、完全なTCP接続を確立せずにポートの状態を調査するため、半開接続を多数生成し、これが今回のバグをトリガーする一因となりました。
  • サービス拒否 (DoS): 正当なユーザーがサービスを利用できないようにするサイバー攻撃の一種です。このケースでは、サーバーが不正なソケット処理にリソースを消費し、正当な接続を処理できなくなることでDoS状態に陥ります。

技術的詳細

Darwinカーネルの accept() バグは、TCP接続の確立プロセスにおける特定の競合状態または内部エラー処理の不備に起因していました。具体的には、クライアントが接続を中断した際に、カーネルが ECONNABORTED エラーを適切に accept() の呼び出し元に伝播させる代わりに、不完全なソケット構造体を返してしまうというものでした。

この「不完全なソケット」とは、ソケットディスクリプタ自体は有効であるものの、そのソケットに関連付けられるべき sockaddr 構造体(リモートピアのアドレス情報を含む)が空であるか、または無効な状態であることを意味します。

Go言語の syscall パッケージ内の Accept 関数は、内部的にC言語の accept システムコールを呼び出した後、返されたソケットディスクリプタとアドレス情報を処理します。この処理の一部として、anyToSockaddr 関数が呼び出され、生の sockaddr データをGoの Sockaddr インターフェース型に変換しようとします。

バグのある accept() の挙動により、anyToSockaddr に渡される sockaddr データが空(len == 0)であるか、または解析不能な状態であったため、anyToSockaddr はそのアドレスファミリーを特定できず、結果として EAFNOSUPPORT エラーを返していました。

この問題は、GoのHTTPサーバーが accept() から返されたソケットを処理する際に、予期せぬ EAFNOSUPPORT エラーに遭遇し、そのソケットを破棄する、またはエラー処理ロジックがトリガーされることで、サーバーのパフォーマンス低下やリソース枯渇を引き起こし、最終的に正当な接続の受け入れを妨げるサービス拒否状態につながっていました。

このコミットは、accept() から返されたソケットのアドレス情報の長さ(len)がゼロであるという、このバグに特有の症状を検出し、それを ECONNABORTED エラーとして扱うことで、Goアプリケーションがこのカーネルバグの影響を回避できるようにします。

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

変更は src/pkg/syscall/syscall_bsd.go ファイルの Accept 関数内で行われました。

--- a/src/pkg/syscall/syscall_bsd.go
+++ b/src/pkg/syscall/syscall_bsd.go
@@ -303,6 +303,14 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) {
 	if err != nil {
 		return
 	}\n+\tif len == 0 {\n+\t\t// Accepted socket has no address.\n+\t\t// This is likely due to a bug in xnu kernels,\n+\t\t// where instead of ECONNABORTED error socket\n+\t\t// is accepted, but has no address.\n+\t\tClose(nfd)\n+\t\treturn 0, nil, ECONNABORTED\n+\t}\n 	sa, err = anyToSockaddr(&rsa)\n 	if err != nil {\n 		Close(nfd)\

具体的には、Accept 関数内で accept4 (または accept) システムコールが呼び出され、その結果として新しいファイルディスクリプタ nfd とアドレス情報の長さ len が取得された直後に、以下の8行が追加されました。

コアとなるコードの解説

追加されたコードブロックは以下の通りです。

	if len == 0 {
		// Accepted socket has no address.
		// This is likely due to a bug in xnu kernels,
		// where instead of ECONNABORTED error socket
		// is accepted, but has no address.
		Close(nfd)
		return 0, nil, ECONNABORTED
	}

このコードは、accept() システムコールが成功したと報告したにもかかわらず、返されたソケットに関連付けられたアドレス情報(sockaddr)の長さが 0 であるという、特定の異常な状態をチェックしています。

  1. if len == 0: これは、accept() が新しい接続を受け入れたと報告したが、その接続のリモートピアのアドレス情報(sockaddr)のサイズがゼロである、という異常なケースを検出します。コミットメッセージにあるように、これはDarwinカーネルのバグによって引き起こされる既知の症状です。本来 ECONNABORTED エラーが返されるべき状況で、アドレス情報を持たないソケットが誤って受け入れられてしまうのです。
  2. コメント: 追加されたコメントは、この len == 0 の状態がXNUカーネルのバグによるものであることを明確に説明しています。このバグは、ECONNABORTED エラーの代わりにソケットが受け入れられるが、そのソケットにはアドレスがない、という挙動を示します。
  3. Close(nfd): 誤って受け入れられた(しかし無効な)ソケットディスクリプタ nfd を閉じます。これにより、システムリソースのリークを防ぎ、この無効なソケットが後続の処理で問題を引き起こすのを防ぎます。
  4. return 0, nil, ECONNABORTED: この関数呼び出し(Accept)を、新しいファイルディスクリプタ 0(無効な値)、nilSockaddr、そして ECONNABORTED エラーを返して終了します。これにより、Goのネットワークスタックのより上位の層では、この接続がクライアントによって中断されたものとして適切に処理されるようになります。これは、本来カーネルが返すはずだったエラーをGo側でエミュレートすることで、アプリケーションが予期せぬ EAFNOSUPPORT エラーに遭遇するのを防ぎ、より堅牢なエラーハンドリングを可能にします。

この変更により、GoのHTTPサーバーは、nmapのようなツールによるポートスキャンによって引き起こされる無効な接続試行を適切に処理し、サービス拒否状態に陥ることを回避できるようになりました。

関連リンク

参考にした情報源リンク

  • https://github.com/golang/go/commit/5197fa80405a470eef7cd2540380172aea6d60af
  • accept() system call documentation (general Unix/Linux man pages)
  • sockaddr structure documentation (general Unix/Linux man pages)
  • ECONNABORTED and EAFNOSUPPORT error codes (general Unix/Linux errno man pages)
  • XNU kernel architecture overview (Apple Developer Documentation, general knowledge)
  • nmap documentation (general knowledge of port scanning)
  • Go syscall package source code (contextual understanding)
  • Discussion around Go issue #3849 (if publicly available, for deeper context on the bug's impact and diagnosis)
    • (Note: The original issue link points to Google Code, which has been migrated. Searching for "Go issue 3849" on GitHub might lead to the migrated issue or related discussions.)