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

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

このコミットは、Goランタイムのガベージコレクション(GC)とライブネス解析に関する複数の重要な修正と改善を導入しています。特に、GODEBUG=gcdead=1というデバッグモードの動作を改善し、メモリの不正な使用をより効果的に検出できるようにすることを目的としています。また、ライブネス解析の精度向上、コンパイラの最適化におけるバグ修正、そしてGCが文字列やスライスのような複合型を正確にスキャンできるようにするための変更が含まれています。

コミット

commit 28f1868fed32a38ddd17f71efe4421a246c0b1f1
Author: Russ Cox <rsc@golang.org>
Date:   Thu Apr 3 20:33:25 2014 -0400

    cmd/gc, runtime: make GODEBUG=gcdead=1 mode work with liveness
    
    Trying to make GODEBUG=gcdead=1 work with liveness
    and in particular ambiguously live variables.
    
    1. In the liveness computation, mark all ambiguously live
    variables as live for the entire function, except the entry.
    They are zeroed directly after entry, and we need them not
    to be poisoned thereafter.
    
    2. In the liveness computation, compute liveness (and deadness)
    for all parameters, not just pointer-containing parameters.
    Otherwise gcdead poisons untracked scalar parameters and results.
    
    3. Fix liveness debugging print for -live=2 to use correct bitmaps.
    (Was not updated for compaction during compaction CL.)
    
    4. Correct varkill during map literal initialization.
    Was killing the map itself instead of the inserted value temp.
    
    5. Disable aggressive varkill cleanup for call arguments if
    the call appears in a defer or go statement.
    
    6. In the garbage collector, avoid bug scanning empty
    strings. An empty string is two zeros. The multiword
    code only looked at the first zero and then interpreted
    the next two bits in the bitmap as an ordinary word bitmap.
    For a string the bits are 11 00, so if a live string was zero
    length with a 0 base pointer, the poisoning code treated
    the length as an ordinary word with code 00, meaning it
    needed poisoning, turning the string into a poison-length
    string with base pointer 0. By the same logic I believe that
    a live nil slice (bits 11 01 00) will have its cap poisoned.
    Always scan full multiword struct.
    
    7. In the runtime, treat both poison words (PoisonGC and
    PoisonStack) as invalid pointers that warrant crashes.
    
    Manual testing as follows:
    
    - Create a script called gcdead on your PATH containing:
    
            #!/bin/bash
            GODEBUG=gcdead=1 GOGC=10 GOTRACEBACK=2 exec "$@"
    - Now you can build a test and then run 'gcdead ./foo.test'.
    - More importantly, you can run 'go test -short -exec gcdead std'
       to run all the tests.
    
    Fixes #7676.
    
    While here, enable the precise scanning of slices, since that was
    disabled due to bugs like these. That now works, both with and
    without gcdead.
    
    Fixes #7549.
    
    LGTM=khr
    R=khr
    CC=golang-codereviews
    https://golang.org/cl/83410044

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

https://github.com/golang/go/commit/28f1868fed32a38ddd17f71efe4421a246c0b1f1

元コミット内容

このコミットの主な目的は、GODEBUG=gcdead=1というGoランタイムのデバッグモードが、ライブネス解析、特に「曖昧にライブな変数」と連携して正しく機能するようにすることです。このデバッグモードは、本来解放されるべきメモリが誤って参照され続けるようなバグを検出するために、解放されたメモリ領域を「ポイズン」(毒)値で上書きし、その領域へのアクセスがあった場合にプログラムをクラッシュさせることで問題を顕在化させます。

コミットは以下の7つの主要な変更点を含んでいます。

  1. 曖昧にライブな変数の扱い: ライブネス計算において、曖昧にライブな変数を関数全体でライブとしてマークする(ただし、関数エントリー直後を除く)。これにより、エントリー直後にゼロ化されるこれらの変数が、その後ポイズン化されるのを防ぎます。
  2. 全パラメータのライブネス計算: ポインタを含むパラメータだけでなく、すべてのパラメータ(スカラー型も含む)に対してライブネス計算を行う。これにより、gcdeadモードが追跡されていないスカラーパラメータや結果を誤ってポイズン化するのを防ぎます。
  3. ライブネスデバッグ出力の修正: -live=2オプション使用時のライブネスデバッグ出力が、正しいビットマップを使用するように修正。これは、以前のコンパクション関連の変更で更新されていなかった部分です。
  4. マップリテラル初期化時のvarkill修正: マップリテラルの初期化中に発生するvarkill(変数破棄)の誤りを修正。以前は挿入される値の一時変数ではなく、マップ自体を破棄していました。
  5. deferまたはgoステートメント内の引数に対するvarkillの無効化: deferまたはgoステートメント内の関数呼び出し引数に対して、積極的なvarkillによるクリーンアップを無効にする。これにより、これらの特殊なコンテキストでの変数のライフタイム管理が正しく行われるようにします。
  6. 空文字列のスキャンバグ回避: ガベージコレクタが空文字列をスキャンする際のバグを回避。空文字列は2つのゼロで表現されますが、マルチワードスキャンコードが最初のゼロしか見ず、その後のビットを通常のワードビットマップとして解釈していました。これにより、ポイズン化コードが誤って空文字列の長さをポイズン化し、不正な文字列を生成する可能性がありました。同様のロジックで、ライブなnilスライスも誤ってポイズン化される可能性があったため、常にマルチワード構造体全体をスキャンするように変更します。
  7. ポイズンワードのクラッシュトリガー化: ランタイムにおいて、PoisonGCPoisonStackの両方のポイズンワードを、クラッシュを引き起こす無効なポインタとして扱う。

このコミットは、Goの内部デバッグツールであるgcdeadの信頼性を高め、より正確なメモリ管理とバグ検出を可能にすることを目的としています。また、#7676#7549という2つのバグを修正しています。特に#7549は、以前バグのために無効化されていたスライスの正確なスキャンを再び有効にすることを可能にしました。

変更の背景

このコミットの背景には、Goランタイムのガベージコレクション(GC)とメモリ管理の正確性を向上させるという継続的な取り組みがあります。特に、GODEBUG=gcdead=1というデバッグモードは、開発者がメモリ関連のバグを特定するのに役立つ強力なツールですが、その動作にはいくつかの不正確さや制限がありました。

主な問題点は以下の通りです。

  • gcdeadモードの不正確性: gcdead=1モードは、本来解放されるべきメモリ領域を「ポイズン」値で上書きし、その領域への不正なアクセスを検出することで、メモリリークやuse-after-freeのようなバグを顕在化させます。しかし、ライブネス解析の不正確さや、特定のデータ構造(特に空文字列やnilスライス)のGCスキャンにおけるバグにより、正しくないポイズン化が発生したり、本来検出されるべきバグが見過ごされたりする可能性がありました。
  • 曖昧にライブな変数: コンパイラのライブネス解析は、変数がプログラムのどの時点で「ライブ」(将来使用される可能性がある)であるかを判断します。しかし、一部の変数はそのライブネスが「曖昧」であると判断されることがあり、その結果、必要以上に長くライブとして扱われ、gcdeadモードで誤ってポイズン化されることを防ぐ必要がありました。
  • パラメータのライブネス追跡の不足: ポインタを含むパラメータのみがライブネス追跡の対象となっていたため、スカラー型のパラメータがgcdeadモードで誤ってポイズン化される可能性がありました。
  • GCスキャンのバグ: 空文字列やnilスライスのような特定の複合型が、GCによって正しくスキャンされないバグが存在しました。これは、GCがメモリ内のポインタを正確に識別し、追跡する能力に影響を与え、結果としてメモリの不正な解放や保持につながる可能性がありました。
  • varkillの誤動作: コンパイラの内部最適化であるvarkill(変数の破棄)が、マップリテラルの初期化やdefer/goステートメント内の関数呼び出し引数に対して誤って適用されることがあり、これがプログラムの動作に影響を与える可能性がありました。

これらの問題に対処することで、Goランタイムの堅牢性とデバッグ能力を向上させ、より安定したアプリケーション開発を支援することが、このコミットの重要な背景となっています。特に、#7676#7549という具体的なバグ報告が、これらの修正を推進する直接的な要因となりました。

前提知識の解説

このコミットを理解するためには、以下のGoランタイムとコンパイラの概念に関する前提知識が必要です。

  1. ガベージコレクション (GC) とライブネス解析:

    • ガベージコレクション (GC): Goは、メモリ管理を自動化するためにガベージコレクタを使用します。GoのGCは、主に「マーク&スイープ」アルゴリズムに基づいており、プログラムが使用しなくなったメモリ(「デッド」オブジェクト)を自動的に識別し、再利用します。これにより、開発者は手動でのメモリ管理から解放されます。
    • ライブネス解析: コンパイラ最適化の一種で、プログラムの特定の時点でどの変数が「ライブ」(その値が将来の計算で使われる可能性がある)であるかを決定します。GCは、このライブネス情報を使用して、どのオブジェクトが到達可能(ライブな変数から参照されている)であるかを判断し、到達不可能なオブジェクトを回収します。
    • 正確なGC (Precise GC): GoのGCは「型正確」です。これは、GCがメモリ内のすべてのデータ型レイアウトを正確に把握しており、オブジェクト内のどの部分がポインタであり、どの部分が非ポインタデータであるかを正確に識別できることを意味します。これにより、GCは非ポインタデータを誤ってポインタとして解釈するリスクを回避し、メモリの誤った保持や解放を防ぎます。
  2. GODEBUG=gcdead=1:

    • これはGoランタイムの内部的なデバッグモードであり、標準的なドキュメントには記載されていません。その目的は、GCが回収したはずのメモリ領域が、プログラムによって誤って参照されるようなバグ(use-after-freeなど)を検出することです。
    • このモードが有効な場合、GCは回収したメモリ領域を特定の「ポイズン」(毒)値で上書きします。もしプログラムがこのポイズン化された領域にアクセスしようとすると、ランタイムはそれを検出し、通常はプログラムをクラッシュさせます。これにより、開発者はメモリの不正な使用を早期に、かつ明確に特定できます。
  3. 曖昧にライブな変数 (Ambiguously Live Variables):

    • ライブネス解析の過程で、コンパイラが変数のライブネスを完全に確定できない場合があります。このような変数は「曖昧にライブ」と見なされます。
    • コンパイラは安全のために、曖昧な変数を「ライブ」として扱う傾向があります。これは、誤ってデッドと判断してGCに回収させてしまうよりも、メモリを少し長く保持する方が安全であるためです。しかし、gcdeadモードのような厳密なデバッグ環境では、このような曖昧さが問題となることがあります。
  4. スライスとマルチワード構造体:

    • スライス: Goのスライスは、基になる配列へのポインタ、長さ、容量の3つのフィールドを持つ小さなデータ構造(スライスヘッダ)です。GCはスライスヘッダをスキャンし、基になる配列がポインタを含む場合、その配列の要素を再帰的にスキャンします。
    • マルチワード構造体: 複数のマシンワードにまたがる構造体です。Goのコンパイラは、このような構造体内のすべてのポインタフィールドの位置に関するメタデータを生成し、GCがそれらのポインタを正確に追跡できるようにします。
    • 空文字列: Goでは、空文字列はデータポインタがnilで長さが0の構造体として内部的に表現されます。これは、GCがスキャンする際に特別な注意が必要なマルチワード構造体の一例です。
  5. varkill (変数破棄):

    • これはGoコンパイラの内部的な最適化フェーズまたはメカニズムを指す用語であり、一般には公開されていません。その目的は、変数がもはや必要ないことをコンパイラが判断したときに、その変数を「破棄」または「無効化」することです。これにより、メモリの早期解放や、不要な変数がライブネス解析に影響を与えるのを防ぐことができます。
    • このコミットでは、varkillが特定の状況(マップリテラル初期化、defer/goステートメント)で誤動作していた問題に対処しています。

これらの概念を理解することで、コミットがGoのメモリ管理とデバッグ能力をどのように改善しようとしているのか、その技術的な詳細を深く掘り下げることができます。

技術的詳細

このコミットは、Goのコンパイラ(cmd/gc)とランタイム(runtime)の両方にわたる複数の技術的変更を含んでおり、GODEBUG=gcdead=1モードの正確性と堅牢性を向上させることに焦点を当てています。

  1. ライブネス解析の改善 (src/cmd/gc/plive.c):

    • 曖昧にライブな変数の処理: ライブネス解析において、コンパイラがそのライブネスを完全に確定できない「曖昧にライブな変数」が存在します。以前はこれらの変数がgcdeadモードで誤ってポイズン化される可能性がありました。このコミットでは、これらの変数を関数全体(エントリーポイントを除く)で「ライブ」としてマークするように変更します。これにより、関数エントリー直後にゼロ化されるこれらの変数が、その後gcdeadによってポイズン化されることを防ぎ、誤検出を回避します。具体的には、livenessepilogue関数内でambigビットマップが導入され、曖昧な変数のオフセットが記録され、その後のライブネスビットマップに反映されます。
    • 全パラメータのライブネス計算: 以前のライブネス計算は、ポインタを含むパラメータに限定されていました。しかし、gcdeadモードでは、ポインタを含まないスカラー型のパラメータや戻り値も、そのライフタイムが終了した後にポイズン化される必要があります。この変更により、getvariables関数がすべてのパラメータ(PPARAMPPARAMOUT)をライブネス計算の対象に含めるようになります。これにより、gcdeadがスカラーパラメータを誤ってポイズン化するのを防ぎます。
    • デバッグ出力の修正: -live=2オプション使用時のライブネスデバッグ出力が、コンパクション関連の変更で更新されていなかったため、正しいビットマップを使用するように修正されます。これは、livenessprintdebug関数内のpcdataのインデックス計算が修正されたことによります。
  2. varkillの動作修正 (src/cmd/gc/sinit.c, src/cmd/gc/esc.c):

    • マップリテラル初期化時のvarkill: マップリテラルの初期化において、OVARKILL(変数の破棄)が誤ってマップ自体に対して適用されていました。正しい動作は、マップに挿入される値の一時変数に対してvarkillを適用することです。このコミットでは、sinit.c内の関連コードが修正され、OVARKILLval(挿入される値)に対して適用されるようになります。
    • deferおよびgoステートメント内の引数に対するvarkillの無効化: defer(遅延実行)やgo(ゴルーチン起動)ステートメント内の関数呼び出しの引数に対して、コンパイラが積極的なvarkillによるクリーンアップを行うと問題が発生する可能性がありました。これらのコンテキストでは、引数のライフタイムが通常の関数呼び出しとは異なるため、早期の破棄が不正なメモリアクセスにつながる可能性があります。esc.c内のesccall関数が修正され、up->opODEFERまたはOPROCgoステートメントに対応)の場合、varkillの適用を抑制する条件が追加されます。
  3. GCスキャンロジックの改善 (src/pkg/runtime/mgc0.c):

    • 空文字列とnilスキャンのバグ修正: 空文字列は内部的にデータポインタがnilで長さが0の構造体として表現されます。GCのマルチワードスキャンコードが、この構造体の最初のワード(データポインタ)しか見ず、その後のビットを通常のワードビットマップとして誤って解釈していました。これにより、gcdeadモードで空文字列の長さフィールドが誤ってポイズン化され、不正な文字列が生成される可能性がありました。同様に、ライブなnilスライスも誤ってポイズン化される可能性がありました。
    • このコミットでは、scanbitvector関数内のBitsMultiWordケースの処理が修正されます。以前はscanpPtrSizeだけ進めてからStringSliceの処理を行っていましたが、修正後はpscanpに直接設定し、StringSliceの構造体全体を正しくスキャンするように変更されます。これにより、空文字列やnilスライスのような複合型がGCによって正確に処理され、gcdeadモードでの誤検出が防止されます。また、以前バグのために無効化されていたスライスの正確なスキャンが再び有効になります。
  4. ポイズンワードの定義と処理 (src/pkg/runtime/malloc.h, src/pkg/runtime/mgc0.c, src/pkg/runtime/stack.c):

    • 新しいポイズン値の導入: PoisonPtrという単一のポイズン値に代わり、PoisonGCPoisonStackという2つの異なるポイズン値が導入されます。PoisonGCはGCによってポイズン化されたヒープメモリを、PoisonStackはスタックメモリをポイズン化するために使用されます。
    • ポイズン値の厳密なチェック: ランタイムのGCスキャン(scanbitvector)とスタックポインタ調整(adjustpointers)のロジックが更新され、PoisonGCまたはPoisonStackのいずれかの値を持つポインタが検出された場合、それを無効なポインタとして扱い、プログラムをクラッシュさせるように変更されます。これにより、gcdeadモードがより確実にメモリの不正使用を検出できるようになります。

これらの変更は、Goのコンパイラとランタイムの深い部分に触れており、メモリ管理の正確性とデバッグ能力を大幅に向上させるものです。

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

このコミットにおける主要なコード変更は以下のファイルに集中しています。

  • src/cmd/gc/esc.c: エスケープ解析に関連するコード。defergoステートメント内の関数呼び出し引数に対するvarkillの挙動を修正。
  • src/cmd/gc/plive.c: ライブネス解析の主要なロジック。曖昧にライブな変数の扱いと、全パラメータのライブネス計算、デバッグ出力の修正。
  • src/cmd/gc/sinit.c: 初期化処理に関連するコード。マップリテラル初期化時のvarkillの誤りを修正。
  • src/pkg/runtime/malloc.h: メモリ割り当てとGCに関連するヘッダファイル。新しいポイズン値PoisonGCPoisonStackの定義。
  • src/pkg/runtime/mgc0.c: ガベージコレクタのコアロジック。空文字列やスライスのスキャンバグの修正、ポイズン値のチェックロジックの更新。
  • src/pkg/runtime/stack.c: スタック管理に関連するコード。スタックポイズン値のチェックロジックの更新。
  • test/live.go: ライブネス解析のテストケース。新しいテストケースの追加と既存のテストケースの修正により、曖昧にライブな変数やdefer/goステートメントの挙動を検証。

これらのファイルは、Goのコンパイラがコードを解析し、ライブネス情報を生成し、ランタイムがメモリを管理し、GCを実行する上で中心的な役割を担っています。

コアとなるコードの解説

変更された各ファイルの主要なコード変更点とその解説です。

src/cmd/gc/esc.c

このファイルはエスケープ解析(変数がヒープにエスケープするかどうかを判断する)に関連しています。 変更点: esccall関数のシグネチャにNode *up引数が追加され、呼び出し元のノード情報が渡されるようになりました。 これにより、関数呼び出しがdefer (ODEFER) またはgo (OPROC) ステートメント内にある場合に、引数に対する積極的なvarkill(変数の破棄)を抑制するロジックが追加されました。

// 変更前
// if(escassignfromtag(e, t->note, n->escretval, src) == EscNone) {
// 変更後
if(escassignfromtag(e, t->note, n->escretval, src) == EscNone && up->op != ODEFER && up->op != OPROC) {

この変更により、defergoの引数が、それらのステートメントの特殊な実行セマンティクスに合わせて適切に扱われるようになり、早期に破棄されることによる潜在的なバグが回避されます。

src/cmd/gc/plive.c

ライブネス解析の主要な実装が含まれています。 変更点1: getvariables関数において、ポインタを含む変数だけでなく、すべてのパラメータ(PPARAM, PPARAMOUT)がライブネス計算の対象に含まれるようになりました。

// 変更前
// 			case PAUTO:
// 			case PPARAM:
// 			case PPARAMOUT:
// 				if(haspointers(ll->n->type))
// 					arrayadd(result, &ll->n);
// 				break;
// 変更後
			case PAUTO:
				if(haspointers(ll->n->type))
					arrayadd(result, &ll->n);
				break;
			case PPARAM:
			case PPARAMOUT:
				arrayadd(result, &ll->n);
				break;

これにより、GODEBUG=gcdead=1モードでスカラーパラメータが誤ってポイズン化されるのを防ぎます。

変更点2: livenessepilogue関数において、「曖昧にライブな変数」を追跡するためのambigビットマップが導入され、これらの変数が関数全体でライブとして扱われるように修正されました。

// 変更前
// 	Bvec *livein, *liveout, *uevar, *varkill, *args, *locals, *avarinit, *any, *all;
// 変更後
	Bvec *ambig, *livein, *liveout, *uevar, *varkill, *args, *locals, *avarinit, *any, *all;
// ...
// 変更前
// 	// Show live pointer bitmaps.
// 	// We're interpreting the args and locals bitmap instead of liveout so that we
// 変更後
				// Ambiguously live variables are zeroed immediately after
				// function entry. Mark them live for all the non-entry bitmaps
				// so that GODEBUG=gcdead=1 mode does not poison them.
				if(p->as == ACALL)
					bvor(locals, locals, ambig);

ambigビットマップは、曖昧な変数のオフセットを記録し、ACALL(関数呼び出し)命令のライブネスビットマップにbvor(ビットOR)操作で結合されます。これにより、これらの変数がgcdeadモードでポイズン化されることを防ぎます。

変更点3: livenessprintdebug関数において、ライブネスデバッグ出力が正しいビットマップを使用するように修正されました。

// 変更前
// 				args = *(Bvec**)arrayget(lv->argslivepointers, nsafe);
// 				locals = *(Bvec**)arrayget(lv->livepointers, nsafe);
// 				nsafe++;
// 変更後
				args = *(Bvec**)arrayget(lv->argslivepointers, pcdata);
				locals = *(Bvec**)arrayget(lv->livepointers, pcdata);

nsafeの代わりにpcdataインデックスを使用することで、正確なライブネス情報がデバッグ出力に反映されます。

src/cmd/gc/sinit.c

初期化処理に関連するコードです。 変更点: マップリテラルの初期化時にOVARKILLが誤った変数に適用されていたバグを修正。

// 変更前
// 	a = nod(OVARKILL, var, N);
// 変更後
	a = nod(OVARKILL, val, N);

keyvalのペアでマップに要素を挿入する際、OVARKILLvar(マップ自体)ではなく、val(挿入される値の一時変数)に対して適用されるように修正されました。

src/pkg/runtime/malloc.h

メモリ割り当てとGCに関連するヘッダファイルです。 変更点: 新しいポイズン値PoisonGCPoisonStackが定義されました。

// 変更前
// #define PoisonPtr ((uintptr)0x6969696969696969LL)
// 変更後
#define PoisonGC ((uintptr)0xf969696969696969ULL)
#define PoisonStack ((uintptr)0x6868686868686868ULL)

これにより、GCによってポイズン化されたヒープメモリと、スタックメモリのポイズン化を区別できるようになります。

src/pkg/runtime/mgc0.c

ガベージコレクタのコアロジックが含まれています。 変更点1: scanbitvector関数において、BitsDeadの場合にPoisonGCを使用するように変更。

// 変更前
// 				*(uintptr*)scanp = PoisonPtr;
// 変更後
				*(uintptr*)scanp = PoisonGC;

変更点2: BitsPointerの場合のポイズン値チェックにPoisonGCPoisonStackの両方を含めるように変更。

// 変更前
// 					if(precise && (p < (byte*)PageSize || (uintptr)p == PoisonPtr)) {
// 変更後
					if(precise && (p < (byte*)PageSize || (uintptr)p == PoisonGC || (uintptr)p == PoisonStack)) {

変更点3: BitsMultiWordの場合の空文字列とスライスのスキャンロジックを修正。

// 変更前 (簡略化)
// 			case BitsMultiWord:
// 				p = *(byte**)scanp;
// 				// ... スキャンロジック ...
// 				switch(word & 3) {
// 				case BitsString:
// 					if(((String*)(scanp - PtrSize))->len != 0)
// 						markonly(p);
// 					break;
// 				case BitsSlice:
// 					// ... スライスチェック ...
// 					if(((Slice*)(scanp - PtrSize))->cap != 0)
// 						enqueue1(wbufp, (Obj){scanp - PtrSize, PtrSize, 0});
// 					break;
// 				// ...
// 				}
// 変更後 (簡略化)
			case BitsMultiWord:
				p = scanp; // scanpを直接pに設定
				word >>= BitsPerPointer;
				scanp += PtrSize;
				i--;
				// ... ビットマップの読み込みロジック ...
				switch(word & 3) {
				case BitsString:
					if(((String*)p)->len != 0) // pを直接使用
						markonly(((String*)p)->str);
					break;
				case BitsSlice:
					// ... スライスチェック ...
					if(((Slice*)p)->cap != 0) // pを直接使用
						enqueue1(wbufp, (Obj){p, PtrSize, 0});
					break;
				// ...
				}

この修正により、空文字列やnilスライスのようなマルチワード構造体がGCによって正確にスキャンされ、gcdeadモードでの誤検出が防止されます。

src/pkg/runtime/stack.c

スタック管理に関連するコードです。 変更点: adjustpointers関数において、スタックポイズン値としてPoisonStackを使用し、ポイズン値のチェックにPoisonGCPoisonStackの両方を含めるように変更。

// 変更前
// 			if(runtime·debug.gcdead)
// 				scanp[i] = (byte*)0x6868686868686868LL;
// 変更後
			if(runtime·debug.gcdead)
				scanp[i] = (byte*)PoisonStack;
// ...
// 変更前
// 			if(f != nil && (byte*)0 < p && (p < (byte*)PageSize || (uintptr)p == PoisonPtr)) {
// 変更後
			if(f != nil && (byte*)0 < p && (p < (byte*)PageSize || (uintptr)p == PoisonGC || (uintptr)p == PoisonStack)) {

これにより、スタック上のポイズン化されたメモリへのアクセスがより確実に検出され、クラッシュを引き起こすようになります。

test/live.go

ライブネス解析のテストケースです。 変更点: f3, f27defer, f27go関数に新しいコメントとERRORディレクティブが追加され、曖昧にライブな変数やdefer/goステートメント内の変数のライブネス挙動がgcdeadモードで正しく検証されるようになりました。

// f3関数の変更例
// 変更前
// 	print(0)
// 変更後
// 	// Because x and y are ambiguously live, they appear
// 	// live throughout the function, to avoid being poisoned
// 	// in GODEBUG=gcdead=1 mode.
//
// 	print(0) // ERROR "live at call to printint: x y$"

これらのテストケースは、コミットによって修正されたライブネス解析とgcdeadモードの動作を検証するために重要です。

関連リンク

  • Go issue #7676: このコミットによって修正された、gcdeadモードに関連する一般的な問題。
  • Go issue #7549: このコミットによって修正された、スライスの正確なスキャンに関連する問題。

参考にした情報源リンク