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

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

このコミットは、Goランタイムにおけるヒープ破損のバグを修正するものです。具体的には、新しいM(OSスレッド)が作成される際に、そのMに関連付けられたG(ゴルーチン)がガベージコレクタの対象リストに正しく追加されていなかった問題を解決します。

コミット

commit 94fab3cad33b7d5eb7422d319fd88b2db9896a75
Author: Dmitriy Vyukov <dvyukov@google.com>
Date:   Thu Feb 21 21:59:46 2013 +0400

    runtime: fix heap corruption
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/7397049
---
 src/pkg/runtime/proc.c | 6 ++++++\n 1 file changed, 6 insertions(+)

diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 5c36ddf745..9909182b6b 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -972,6 +972,12 @@ runtime·newextram(void)\n 	mp->locked = LockInternal;\n 	mp->lockedg = gp;\n 	gp->lockedm = mp;\n+	// put on allg for garbage collector\n+	if(runtime·lastg == nil)\n+		runtime·allg = gp;\n+	else\n+		runtime·lastg->alllink = gp;\n+	runtime·lastg = gp;\n 	schedunlock();\n 
 	// Add m to the extra list.\n

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

https://github.com/golang/go/commit/94fab3cad33b7d5eb7422d319fd88b2db9896a75

元コミット内容

runtime: fix heap corruption

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7397049

変更の背景

Goランタイムは、M(OSスレッド)、P(プロセッサ)、G(ゴルーチン)という3つの主要な抽象化を用いて並行処理を管理しています。MはOSスレッドを表し、PはGoコードを実行するための論理プロセッサを表し、Gは軽量な実行単位であるゴルーチンを表します。

runtime·newextram関数は、新しいM(OSスレッド)を作成し、それをGoランタイムのスケジューラに統合する役割を担っています。この関数が呼び出されるのは、既存のMがすべてビジーであるか、または特定のシステムコールをブロックしている場合など、追加のOSスレッドが必要になったときです。

このコミットが修正する問題は、runtime·newextramによって新しく作成されたMに紐付けられるゴルーチン(gp)が、ガベージコレクタ(GC)がスキャンすべきゴルーチンのグローバルリスト(allg)に正しく追加されていなかったことに起因します。GCは、プログラムが使用しているメモリを特定し、不要になったメモリを解放する役割を担っています。GCが正しく動作するためには、到達可能なすべてのオブジェクトを追跡できる必要があります。ゴルーチンのスタックは、他のオブジェクトへの参照を保持している可能性があるため、GCはすべての活動中のゴルーチンをスキャンする必要があります。

もし新しく作成されたゴルーチンがallgリストに追加されないと、GCはそのゴルーチンを「見つける」ことができません。その結果、そのゴルーチンが保持しているメモリや、そのゴルーチンから参照されている他のオブジェクトが、GCによって到達不可能と誤って判断され、解放されてしまう可能性があります。これは「ヒープ破損」として現れ、プログラムのクラッシュ、予期せぬ動作、またはデータの破損につながる非常に深刻なバグです。

前提知識の解説

  • Goランタイム: Goプログラムの実行を管理する低レベルのシステム。スケジューラ、ガベージコレクタ、メモリ管理などが含まれます。C言語とGo言語で実装されています。
  • M, P, Gモデル: Goの並行処理モデルの核心。
    • G (Goroutine): Goにおける軽量な実行単位。数KBのスタックを持ち、数百万個作成することも可能。
    • P (Processor): Goコードを実行するための論理プロセッサ。GをM上で実行するためのコンテキストを提供します。Pの数は通常、CPUコア数に制限されます。
    • M (Machine/OS Thread): 実際のOSスレッド。PとGをOS上で実行します。Mは複数のPを切り替えて使用したり、システムコールでブロックされたりすることがあります。
  • ガベージコレクタ (GC): プログラムが動的に確保したメモリのうち、もはや使用されていない(到達不可能になった)ものを自動的に解放するシステム。GoのGCは並行かつ低遅延で動作するように設計されています。
  • allgリスト: Goランタイム内部で管理される、現在活動中のすべてのゴルーチン(G)を追跡するためのグローバルなリンクリスト。GCは、このallgリストを辿ることで、すべてのゴルーチンのスタックをスキャンし、到達可能なメモリを特定します。
  • ヒープ破損 (Heap Corruption): プログラムがヒープメモリ(動的に確保されるメモリ領域)を不適切に操作した結果、メモリの内容が破壊される状態。これは通常、解放済みのメモリへのアクセス(use-after-free)、バッファオーバーフロー、またはGCの不適切な動作によって引き起こされます。ヒープ破損は、セキュリティ脆弱性やプログラムの不安定性の主要な原因となります。
  • runtime·lastg: allgリストの末尾を指すポインタ。新しいゴルーチンをリストに追加する際に使用されます。
  • alllink: g構造体内のフィールドで、allgリンクリストの次の要素へのポインタとして機能します。

技術的詳細

このコミットは、src/pkg/runtime/proc.cファイル内のruntime·newextram関数に6行のコードを追加しています。この関数は、新しいM(OSスレッド)を初期化し、それに紐付けられた初期ゴルーチン(gp)を設定する責任があります。

追加されたコードの目的は、この新しく作成されたゴルーチンgpを、ガベージコレクタが使用するグローバルなゴルーチンリストallgに確実に登録することです。

具体的な追加ロジックは以下の通りです。

  1. // put on allg for garbage collector: コメントが示すように、このセクションはガベージコレクタのためにgpallgリストに追加するものです。
  2. if(runtime·lastg == nil): runtime·lastgallgリストの最後のゴルーチンを指します。もしruntime·lastgnilであれば、これはallgリストが現在空であることを意味します。
  3. runtime·allg = gp;: allgリストが空の場合、新しく作成されたゴルーチンgpがリストの最初の要素(かつ唯一の要素)となります。したがって、runtime·allg(リストの先頭)はgpを指すように設定されます。
  4. else runtime·lastg->alllink = gp;: allgリストが空でない場合、新しく作成されたゴルーチンgpはリストの末尾に追加されます。これは、現在のリストの最後のゴルーチン(runtime·lastgが指す)のalllinkフィールドをgpに設定することで実現されます。これにより、gpがリストの新しい最後の要素としてリンクされます。
  5. runtime·lastg = gp;: 最後に、runtime·lastgポインタは、新しく追加されたゴルーチンgpを指すように更新されます。これにより、次回新しいゴルーチンが追加される際に、正しくリストの末尾にリンクできるようになります。

この変更により、runtime·newextramによって作成されるすべてのゴルーチンが、GCによって確実にスキャンされるようになり、GCがこれらのゴルーチンに関連するメモリを誤って解放する可能性が排除され、結果としてヒープ破損が防止されます。

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

src/pkg/runtime/proc.cファイルのruntime·newextram関数内に以下の6行が追加されました。

@@ -972,6 +972,12 @@ runtime·newextram(void)\n 	mp->locked = LockInternal;\n 	mp->lockedg = gp;\n 	gp->lockedm = mp;\n+	// put on allg for garbage collector\n+	if(runtime·lastg == nil)\n+		runtime·allg = gp;\n+	else\n+		runtime·lastg->alllink = gp;\n+	runtime·lastg = gp;\n 	schedunlock();\n 
 	// Add m to the extra list.\n

コアとなるコードの解説

追加されたコードは、Goランタイムのガベージコレクタがすべての活動中のゴルーチンを追跡するために使用するallgリンクリストの整合性を保証します。

  • runtime·allg: グローバルなg(ゴルーチン)のリンクリストの先頭ポインタ。
  • runtime·lastg: グローバルなgのリンクリストの末尾ポインタ。
  • gp: runtime·newextram関数によって新しく作成され、現在のM(OSスレッド)に関連付けられたゴルーチン。
  • gp->alllink: g構造体内のフィールドで、allgリンクリストにおける次のゴルーチンへのポインタ。

このコードブロックは、新しいゴルーチンgpallgリストの末尾にアトミックに追加する標準的なリンクリスト操作です。

  1. リストが空の場合: runtime·lastg == nilが真の場合、これはallgリストにまだゴルーチンが一つも存在しないことを意味します。この場合、gpがリストの最初のゴルーチンとなるため、runtime·allg(リストの先頭)とruntime·lastg(リストの末尾)の両方がgpを指すように設定されます。
  2. リストが空でない場合: runtime·lastg == nilが偽の場合、リストには既に一つ以上のゴルーチンが存在します。この場合、runtime·lastg->alllink = gp;によって、現在のリストの最後のゴルーチンがgpを指すようにリンクが更新され、gpがリストの新しい末尾となります。その後、runtime·lastg = gp;によってruntime·lastgポインタ自体がgpを指すように更新されます。

この操作により、runtime·newextramが呼び出されるたびに、新しく作成されたゴルーチンがガベージコレクタの監視下に置かれることが保証され、ヒープ破損の原因となる可能性のあるメモリ管理の不整合が解消されます。

関連リンク

  • Goのスケジューラに関するドキュメントや解説: GoのM, P, Gモデルについて深く理解するために役立ちます。
  • Goのガベージコレクタに関するドキュメント: allgリストがGCでどのように使用されるか、GCの動作原理について詳細を知ることができます。

参考にした情報源リンク

  • https://golang.org/cl/7397049 (Goの変更リスト)
  • Go言語の公式ドキュメント (特にランタイムとガベージコレクタのセクション)
  • Goのソースコード (特にsrc/runtime/proc.gosrc/runtime/mgc.goなど、スケジューラやGCに関連するファイル)
  • GoのM, P, Gモデルに関する技術ブログや解説記事 (一般的なGoの並行処理モデルの理解のため)
  • ヒープ破損に関する一般的なコンピュータサイエンスの資料 (ヒープ破損がどのように発生し、なぜ危険なのかを理解するため)

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

このコミットは、Goランタイムにおけるヒープ破損のバグを修正するものです。具体的には、新しいM(OSスレッド)が作成される際に、そのMに関連付けられたG(ゴルーチン)がガベージコレクタの対象リストに正しく追加されていなかった問題を解決します。

コミット

commit 94fab3cad33b7d5eb7422d319fd88b2db9896a75
Author: Dmitriy Vyukov <dvyukov@google.com>
Date:   Thu Feb 21 21:59:46 2013 +0400

    runtime: fix heap corruption
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/7397049
---
 src/pkg/runtime/proc.c | 6 ++++++\n 1 file changed, 6 insertions(+)

diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 5c36ddf745..9909182b6b 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -972,6 +972,12 @@ runtime·newextram(void)\n 	mp->locked = LockInternal;\n 	mp->lockedg = gp;\n 	gp->lockedm = mp;\n+	// put on allg for garbage collector\n+	if(runtime·lastg == nil)\n+		runtime·allg = gp;\n+	else\n+		runtime·lastg->alllink = gp;\n+	runtime·lastg = gp;\n 	schedunlock();\n 
 	// Add m to the extra list.\n

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

https://github.com/golang/go/commit/94fab3cad3cad33b7d5eb7422d319fd88b2db9896a75

元コミット内容

runtime: fix heap corruption

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7397049

変更の背景

Goランタイムは、M(OSスレッド)、P(プロセッサ)、G(ゴルーチン)という3つの主要な抽象化を用いて並行処理を管理しています。MはOSスレッドを表し、PはGoコードを実行するための論理プロセッサを表し、Gは軽量な実行単位であるゴルーチンを表します。

runtime·newextram関数は、新しいM(OSスレッド)を作成し、それをGoランタイムのスケジューラに統合する役割を担っています。この関数が呼び出されるのは、既存のMがすべてビジーであるか、または特定のシステムコールをブロックしている場合など、追加のOSスレッドが必要になったときです。

このコミットが修正する問題は、runtime·newextramによって新しく作成されたMに紐付けられるゴルーチン(gp)が、ガベージコレクタ(GC)がスキャンすべきゴルーチンのグローバルリスト(allg)に正しく追加されていなかったことに起因します。GCは、プログラムが使用しているメモリを特定し、不要になったメモリを解放する役割を担っています。GCが正しく動作するためには、到達可能なすべてのオブジェクトを追跡できる必要があります。ゴルーチンのスタックは、他のオブジェクトへの参照を保持している可能性があるため、GCはすべての活動中のゴルーチンをスキャンする必要があります。

もし新しく作成されたゴルーチンがallgリストに追加されないと、GCはそのゴルーチンを「見つける」ことができません。その結果、そのゴルーチンが保持しているメモリや、そのゴルーチンから参照されている他のオブジェクトが、GCによって到達不可能と誤って判断され、解放されてしまう可能性があります。これは「ヒープ破損」として現れ、プログラムのクラッシュ、予期せぬ動作、またはデータの破損につながる非常に深刻なバグです。

前提知識の解説

  • Goランタイム: Goプログラムの実行を管理する低レベルのシステム。スケジューラ、ガベージコレクタ、メモリ管理などが含まれます。C言語とGo言語で実装されています。
  • M, P, Gモデル: Goの並行処理モデルの核心。
    • G (Goroutine): Goにおける軽量な実行単位。数KBのスタックを持ち、数百万個作成することも可能。
    • P (Processor): Goコードを実行するための論理プロセッサ。GをM上で実行するためのコンテキストを提供します。Pの数は通常、CPUコア数に制限されます。
    • M (Machine/OS Thread): 実際のOSスレッド。PとGをOS上で実行します。Mは複数のPを切り替えて使用したり、システムコールでブロックされたりすることがあります。
  • ガベージコレクタ (GC): プログラムが動的に確保したメモリのうち、もはや使用されていない(到達不可能になった)ものを自動的に解放するシステム。GoのGCは並行かつ低遅延で動作するように設計されています。
  • allgリスト: Goランタイム内部で管理される、現在活動中のすべてのゴルーチン(G)を追跡するためのグローバルなリンクリスト。GCは、このallgリストを辿ることで、すべてのゴルーチンのスタックをスキャンし、到達可能なメモリを特定します。
  • ヒープ破損 (Heap Corruption): プログラムがヒープメモリ(動的に確保されるメモリ領域)を不適切に操作した結果、メモリの内容が破壊される状態。これは通常、解放済みのメモリへのアクセス(use-after-free)、バッファオーバーフロー、またはGCの不適切な動作によって引き起こされます。ヒープ破損は、セキュリティ脆弱性やプログラムの不安定性の主要な原因となります。
  • runtime·lastg: allgリストの末尾を指すポインタ。新しいゴルーチンをリストに追加する際に使用されます。
  • alllink: g構造体内のフィールドで、allgリンクリストの次の要素へのポインタとして機能します。

技術的詳細

このコミットは、src/pkg/runtime/proc.cファイル内のruntime·newextram関数に6行のコードを追加しています。この関数は、新しいM(OSスレッド)を初期化し、それに紐付けられた初期ゴルーチン(gp)を設定する責任があります。

追加されたコードの目的は、この新しく作成されたゴルーチンgpを、ガベージコレクタが使用するグローバルなゴルーチンリストallgに確実に登録することです。

具体的な追加ロジックは以下の通りです。

  1. // put on allg for garbage collector: コメントが示すように、このセクションはガベージコレクタのためにgpallgリストに追加するものです。
  2. if(runtime·lastg == nil): runtime·lastgallgリストの最後のゴルーチンを指します。もしruntime·lastgnilであれば、これはallgリストが現在空であることを意味します。
  3. runtime·allg = gp;: allgリストが空の場合、新しく作成されたゴルーチンgpがリストの最初の要素(かつ唯一の要素)となります。したがって、runtime·allg(リストの先頭)はgpを指すように設定されます。
  4. else runtime·lastg->alllink = gp;: allgリストが空でない場合、新しく作成されたゴルーチンgpはリストの末尾に追加されます。これは、現在のリストの最後のゴルーチン(runtime·lastgが指す)のalllinkフィールドをgpに設定することで実現されます。これにより、gpがリストの新しい最後の要素としてリンクされます。
  5. runtime·lastg = gp;: 最後に、runtime·lastgポインタは、新しく追加されたゴルーチンgpを指すように更新されます。これにより、次回新しいゴルーチンが追加される際に、正しくリストの末尾にリンクできるようになります。

この変更により、runtime·newextramによって作成されるすべてのゴルーチンが、GCによって確実にスキャンされるようになり、GCがこれらのゴルーチンに関連するメモリを誤って解放する可能性が排除され、結果としてヒープ破損が防止されます。

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

src/pkg/runtime/proc.cファイルのruntime·newextram関数内に以下の6行が追加されました。

@@ -972,6 +972,12 @@ runtime·newextram(void)\n 	mp->locked = LockInternal;\n 	mp->lockedg = gp;\n 	gp->lockedm = mp;\n+	// put on allg for garbage collector\n+	if(runtime·lastg == nil)\n+		runtime·allg = gp;\n+	else\n+		runtime·lastg->alllink = gp;\n+	runtime·lastg = gp;\n 	schedunlock();\n 
 	// Add m to the extra list.\n

コアとなるコードの解説

追加されたコードは、Goランタイムのガベージコレクタがすべての活動中のゴルーチンを追跡するために使用するallgリンクリストの整合性を保証します。

  • runtime·allg: グローバルなg(ゴルーチン)のリンクリストの先頭ポインタ。
  • runtime·lastg: グローバルなgのリンクリストの末尾ポインタ。
  • gp: runtime·newextram関数によって新しく作成され、現在のM(OSスレッド)に関連付けられたゴルーチン。
  • gp->alllink: g構造体内のフィールドで、allgリンクリストにおける次のゴルーチンへのポインタ。

このコードブロックは、新しいゴルーチンgpallgリストの末尾にアトミックに追加する標準的なリンクリスト操作です。

  1. リストが空の場合: runtime·lastg == nilが真の場合、これはallgリストにまだゴルーチンが一つも存在しないことを意味します。この場合、gpがリストの最初のゴルーチンとなるため、runtime·allg(リストの先頭)とruntime·lastg(リストの末尾)の両方がgpを指すように設定されます。
  2. リストが空でない場合: runtime·lastg == nilが偽の場合、リストには既に一つ以上のゴルーチンが存在します。この場合、runtime·lastg->alllink = gp;によって、現在のリストの最後のゴルーチンがgpを指すようにリンクが更新され、gpがリストの新しい末尾となります。その後、runtime·lastg = gp;によってruntime·lastgポインタ自体がgpを指すように更新されます。

この操作により、runtime·newextramが呼び出されるたびに、新しく作成されたゴルーチンがガベージコレクタの監視下に置かれることが保証され、ヒープ破損の原因となる可能性のあるメモリ管理の不整合が解消されます。

関連リンク

  • Goのスケジューラに関するドキュメントや解説: GoのM, P, Gモデルについて深く理解するために役立ちます。
  • Goのガベージコレクタに関するドキュメント: allgリストがGCでどのように使用されるか、GCの動作原理について詳細を知ることができます。

参考にした情報源リンク

  • https://golang.org/cl/7397049 (Goの変更リスト)
  • Go言語の公式ドキュメント (特にランタイムとガベージコレクタのセクション)
  • Goのソースコード (特にsrc/runtime/proc.gosrc/runtime/mgc.goなど、スケジューラやGCに関連するファイル)
  • GoのM, P, Gモデルに関する技術ブログや解説記事 (一般的なGoの並行処理モデルの理解のため)
  • ヒープ破損に関する一般的なコンピュータサイエンスの資料 (ヒープ破損がどのように発生し、なぜ危険なのかを理解するため)