Go、後方互換性、および GODEBUG

はじめに

Go の後方互換性への重点は、その主要な強みの 1 つです。ただし、完全な互換性を維持できない場合もあります。コードがバグのある (安全でないものを含む) 動作に依存している場合、バグを修正するとそのコードが壊れます。新しい機能も同様の影響を与える可能性があります。HTTP クライアントによる HTTP/2 の使用を有効にしたことで、バグのある HTTP/2 実装を持つサーバーに接続するプログラムが壊れました。これらの種類の変更は避けられず、Go 1 互換性ルールによって許可されています。それでも、Go は、新しいツールチェーンを使用して古いコードをコンパイルする Go 開発者へのそのような変更の影響を軽減するための GODEBUG と呼ばれるメカニズムを提供します。

GODEBUG 設定は、Go プログラムの特定の実行部分を制御する key=value ペアです。環境変数 GODEBUG は、これらの設定をコンマ区切りのリストで保持できます。たとえば、Go プログラムが以下の環境で実行されている場合、

GODEBUG=http2client=0,http2server=0

その Go プログラムは、HTTP クライアントと HTTP サーバーの両方で HTTP/2 の使用をデフォルトで無効にします。GODEBUG 環境変数内の認識されない設定は無視されます。特定のプログラムのデフォルト GODEBUG を設定することも可能です (下記参照)。

Go 1 互換性によって許可されているが、既存のプログラムの一部を壊す可能性のある変更を準備する際には、まず、できるだけ多くの既存のプログラムが機能し続けるように変更を設計します。残りのプログラムについては、個々のプログラムが古い動作にオプトインできるようにする新しい GODEBUG 設定を定義します。GODEBUG 設定の追加が実行不可能である場合、追加できないことがありますが、それは極めてまれであるはずです。

互換性のために追加された GODEBUG 設定は、最低 2 年間 (Go の 4 リリース) 維持されます。http2clienthttp2server のように、はるかに長く、場合によっては無期限に維持されるものもあります。

可能な場合、各 GODEBUG 設定には、その設定の非デフォルト値に基づいて特定のプログラムの動作が変更された回数をカウントする /godebug/non-default-behavior/<name>:events という名前の関連する runtime/metrics カウンターがあります。たとえば、GODEBUG=http2client=0 が設定されている場合、/godebug/non-default-behavior/http2client:events は、プログラムが HTTP/2 サポートなしで構成した HTTP トランスポートの数をカウントします。

GODEBUG のデフォルト値

GODEBUG 設定が環境変数にリストされていない場合、その値は 3 つのソースから導き出されます。プログラムのビルドに使用される Go ツールチェーンのデフォルト値で、go.mod にリストされている Go バージョンと一致するように修正され、その後、プログラム内の明示的な //go:debug 行によって上書きされます。

GODEBUG 履歴は、各 Go ツールチェーン バージョンの正確なデフォルト値を示しています。たとえば、Go 1.21 は panic(nil) が許可されるかどうかを制御する panicnil 設定を導入しました。これは panicnil=0 がデフォルトで、panic(nil) を実行時エラーにします。panicnil=1 を使用すると、Go 1.20 以前の動作に戻ります。

古い Go バージョンを宣言するワークモジュールまたはワークスペースをコンパイルする場合、Go ツールチェーンは、その古い Go バージョンにできるだけ近づくようにデフォルト値を修正します。たとえば、Go 1.21 ツールチェーンがプログラムをコンパイルする場合、ワークモジュールの go.mod またはワークスペースの go.workgo 1.20 と記述されていると、プログラムは Go 1.21 ではなく Go 1.20 に合わせて panicnil=1 がデフォルトになります。

この GODEBUG デフォルト設定方法は Go 1.21 でのみ導入されたため、Go 1.20 より前の Go バージョンをリストしているプログラムは、古いバージョンではなく Go 1.20 に一致するように構成されます。

これらのデフォルトを上書きするには、Go 1.23 から、ワークモジュールの go.mod またはワークスペースの go.work に 1 つ以上の godebug 行をリストできます。

godebug (
    default=go1.21
    panicnil=1
    asynctimerchan=0
)

特別なキー default は、指定されていない設定を取得する Go バージョンを示します。これにより、モジュールの Go 言語バージョンとは別に GODEBUG のデフォルトを設定できます。この例では、プログラムは Go 1.21 のセマンティクスを要求し、次に Go 1.21 以前の古い panic(nil) 動作と新しい Go 1.23 の asynctimerchan=0 動作を要求しています。

godebug ディレクティブについては、ワークモジュールの go.mod のみが参照されます。必要な依存モジュール内のディレクティブはすべて無視されます。認識されない設定で godebug をリストするとエラーになります。(Go 1.23 より古いツールチェーンは godebug を全く理解しないため、すべての godebug 行を拒否します。) ワークスペースが使用されている場合、go.mod ファイル内の godebug ディレクティブは無視され、代わりに go.workgodebug ディレクティブについて参照されます。

go および godebug 行からのデフォルトは、ビルドされるすべてのメインパッケージに適用されます。よりきめ細やかな制御のために、Go 1.21 から、メインパッケージのソースファイルは、ファイルの先頭 (package ステートメントの前) に 1 つ以上の //go:debug ディレクティブを含めることができます。前の例の godebug 行は次のように記述されます。

//go:debug default=go1.21
//go:debug panicnil=1
//go:debug asynctimerchan=0

Go 1.21 以降、Go ツールチェーンは、認識されない GODEBUG 設定を持つ //go:debug ディレクティブを無効なプログラムとして扱います。特定の GODEBUG 設定に対して複数の //go:debug 行を持つプログラムも無効として扱われます。(古いツールチェーンは //go:debug ディレクティブを完全に無視します。)

メインパッケージにコンパイルされるデフォルトは、コマンドによって報告されます。

go list -f '{{.DefaultGODEBUG}}' my/main/package

基本 Go ツールチェーンのデフォルトからの違いのみが報告されます。

パッケージをテストする場合、*_test.go ファイル内の //go:debug 行は、テストのメインパッケージに対するディレクティブとして扱われます。それ以外のコンテキストでは、//go:debug 行はツールチェーンによって無視されます。go vet は、そのような行を不適切な場所にあるものとして報告します。

GODEBUG 履歴

このセクションでは、互換性の理由で各主要な Go リリースで導入および削除された GODEBUG 設定について説明します。パッケージまたはプログラムは、内部デバッグ目的で追加の設定を定義する場合があります。たとえば、ランタイムドキュメントおよびgo コマンドドキュメントを参照してください。

Go 1.25

Go 1.25 は、Go ランタイムが OS 匿名メモリマッピングにその目的に関するコンテキストを注釈付けするかどうかを制御する新しい decoratemappings 設定を追加しました。これらの注釈は、/proc/self/maps および /proc/self/smaps に "[anon: Go: ...]" として表示されます。この設定は Linux でのみ使用されます。Go 1.25 では、decoratemappings=1 がデフォルトで、注釈を有効にします。decoratemappings=0 を使用すると、Go 1.25 以前の動作に戻ります。この設定はプログラムの起動時に固定され、プログラムの起動後に GODEBUG 環境変数を変更しても変更できません。

Go 1.25 は、Go コマンドがファイルを埋め込む通常のファイルへのシンボリックリンクをたどるかどうかを制御する新しい embedfollowsymlinks 設定を追加しました。デフォルト値 embedfollowsymlinks=0 は、シンボリックリンクをたどることを許可しません。embedfollowsymlinks=1 は、シンボリックリンクをたどることを許可します。

Go 1.25 は、Go ランタイムがデフォルトの GOMAXPROCS を設定するときに cgroup CPU 制限を考慮するかどうかを制御する新しい containermaxprocs 設定を追加しました。デフォルト値 containermaxprocs=1 は、合計論理 CPU 数と CPU アフィニティに加えて cgroup 制限を使用します。containermaxprocs=0 は、cgroup 制限の考慮を無効にします。この設定は Linux にのみ影響します。

Go 1.25 は、Go ランタイムが新しい CPU アフィニティまたは cgroup 制限のために GOMAXPROCS を定期的に更新するかどうかを制御する新しい updatemaxprocs 設定を追加しました。デフォルト値 updatemaxprocs=1 は、定期的な更新を有効にします。updatemaxprocs=0 は、定期的な更新を無効にします。

Go 1.25 は、RFC 9155 に従って TLS 1.2 で SHA-1 署名アルゴリズムを無効にしました。デフォルトは tlssha1=1 設定を使用して元に戻すことができます。

Go 1.25 は、crypto/x509.CreateCertificate で不足している SubjectKeyId を埋めるために SHA-256 に切り替えました。設定 x509sha256skid=0 は SHA-1 に戻ります。

Go 1.25 は、ランタイム内部ロックの競合レポートのセマンティクスを修正したため、runtimecontentionstacks 設定を削除しました。

Go 1.25 (Go 1.25 RC 2 以降) は、VCS インジェクション攻撃に関する懸念から、複数の VCS が検出された場合のビルド情報スタンプを無効にしました。この動作と設定は、Go 1.24.5 および Go 1.23.11 にバックポートされました。この動作は、設定 allowmultiplevcs=1 で再度有効にすることができます。

Go 1.24

Go 1.24 は、Go 暗号モジュールが FIPS 140-3 モードで動作するかどうかを制御する新しい fips140 設定を追加しました。可能な値は次のとおりです。

  • "off": FIPS 140-3 モードの特別なサポートなし。これがデフォルトです。
  • "on": Go 暗号モジュールは FIPS 140-3 モードで動作します。
  • "only": "on" と同様ですが、FIPS 140-3 によって承認されていない暗号アルゴリズムはエラーを返すかパニックを引き起こします。詳細については、FIPS 140-3 準拠を参照してください。この設定はプログラムの起動時に固定され、プログラムの起動後に GODEBUG 環境変数を変更しても変更できません。

Go 1.24 は、グローバルな math/rand.Seed を no-op に変更しました。この動作は randseednop 設定によって制御されます。Go 1.24 では、randseednop=1 がデフォルトです。randseednop=0 を使用すると、Go 1.24 以前の動作に戻ります。

Go 1.24 は、multipathtcp 設定に新しい値を追加しました。multipathtcp の可能な値は次のとおりです。

  • "0": デフォルトでダイアラーとリスナーで MPTCP を無効にする
  • "1": デフォルトでダイアラーとリスナーで MPTCP を有効にする
  • "2": デフォルトでリスナーでのみ MPTCP を有効にする
  • "3": デフォルトでダイアラーでのみ MPTCP を有効にする

Go 1.24 では、multipathtcp="2" がデフォルトになり、リスナーでデフォルトで有効になります。multipathtcp="0" を使用すると、Go 1.24 以前の動作に戻ります。

Go 1.24 は、go test -json の動作を、ビルドエラーをテキストではなく JSON として出力するように変更しました。これらの新しい JSON イベントは新しい Action 値によって区別されますが、これらのイベントに対して堅牢でない CI システムで問題を引き起こす可能性があります。この動作は gotestjsonbuildtext 設定で制御できます。gotestjsonbuildtext=1 を使用すると、1.23 の動作に戻ります。この設定は将来のリリース (最短で Go 1.28) で削除されます。

Go 1.24 は、crypto/rsa を変更し、RSA キーが少なくとも 1024 ビットであることを要求するようにしました。この動作は rsa1024min 設定で制御できます。rsa1024min=0 を使用すると、Go 1.23 の動作に戻ります。

Go 1.24 は、crypto/subtle パッケージでプラットフォーム固有のデータ独立タイミング (DIT) モードを有効にするメカニズムを導入しました。このモードは、dataindependenttiming 設定を使用してプログラム全体で有効にできます。Go 1.24 では、dataindependenttiming=0 がデフォルトです。dataindependenttiming が設定されていない場合、Go 1.23 からのデフォルトの動作に変化はありません。dataindependenttiming=1 を使用すると、Go プログラム全体で DIT モードが有効になります。有効にすると、Go から C を呼び出すときに DIT が有効になります。有効にすると、C から Go コードを呼び出すときに DIT が有効になり、Go コードに入るときに有効になっていなかった場合は、C に戻る前に無効になります。これは現在、arm64 プログラムにのみ影響します。他のすべてのプラットフォームでは no-op です。

Go 1.24 は x509sha1 設定を削除しました。crypto/x509 は、SHA-1 ベースの署名アルゴリズムを使用する証明書の署名検証をサポートしなくなりました。

Go 1.24 は、x509usepolicies 設定のデフォルト値を 0 から 1 に変更しました。証明書をマーシャリングするとき、ポリシーはデフォルトで Certificate.PolicyIdentifiers フィールドではなく、Certificate.Policies フィールドから取得されるようになりました。

Go 1.24 は、ポスト量子鍵交換メカニズム X25519MLKEM768 をデフォルトで有効にしました。デフォルトは、tlsmlkem 設定を使用して元に戻すことができます。これは、大きなレコードを正しく処理せず、ハンドシェイク中にタイムアウトを引き起こすバグのある TLS サーバーを扱う場合に役立ちます (TLS post-quantum TL;DR fail を参照)。Go 1.24 は、X25519Kyber768Draft00 および Go 1.23 の tlskyber 設定も削除しました。

Go 1.24 は、ParsePKCS1PrivateKey を変更し、エンコードされた秘密鍵内の CRT パラメータを使用および検証するようにしました。この動作は x509rsacrt 設定で制御できます。x509rsacrt=0 を使用すると、Go 1.23 の動作に戻ります。

Go 1.23

Go 1.23 は、time パッケージによって作成されるチャネルをバッファなし (同期) に変更しました。これにより、Timer.Stop および Timer.Reset メソッドの結果を正しく使用することがはるかに簡単になります。asynctimerchan 設定はこの変更を無効にします。この変更に対するランタイムメトリクスはありません。この設定は将来のリリース (最短で Go 1.27) で削除される可能性があります。

Go 1.23 は、再解析ポイントの os.Lstat および os.Stat によって報告されるモードビットを変更しました。これは winsymlink 設定で制御できます。Go 1.23 (winsymlink=1) 以降、マウントポイントには os.ModeSymlink が設定されなくなり、シンボリックリンク、Unix ソケット、または重複排除ファイルではない再解析ポイントには常に os.ModeIrregular が設定されるようになりました。これらの変更の結果、filepath.EvalSymlinks はマウントポイントを評価しなくなりました。これは多くの不整合とバグの原因でした。以前のバージョン (winsymlink=0) では、マウントポイントはシンボリックリンクとして扱われ、非デフォルトの os.ModeType ビット (例: os.ModeDir) を持つ他の再解析ポイントには ModeIrregular ビットが設定されていません。

Go 1.23 は、os.Readlink および filepath.EvalSymlinks を変更し、ボリュームをドライブレターに正規化しようとすることを回避するようにしました。これは常に可能であるとは限りませんでした。この動作は winreadlinkvolume 設定によって制御されます。Go 1.23 では、winreadlinkvolume=1 がデフォルトです。以前のバージョンでは winreadlinkvolume=0 がデフォルトでした。

Go 1.23 は、実験的なポスト量子鍵交換メカニズム X25519Kyber768Draft00 をデフォルトで有効にしました。デフォルトは、tlskyber 設定を使用して元に戻すことができます。これは、大きなレコードを正しく処理せず、ハンドシェイク中にタイムアウトを引き起こすバグのある TLS サーバーを扱う場合に役立ちます (TLS post-quantum TL;DR fail を参照)。

Go 1.23 は、crypto/x509.ParseCertificate の動作を変更し、負のシリアル番号を拒否するようにしました。この変更は、x509negativeserial 設定で元に戻すことができます。

Go 1.23 は、html/template での ECMAScript 6 テンプレートリテラルのサポートをデフォルトで再度有効にしました。jstmpllitinterp 設定はもはや効果がありません。

Go 1.23 は、明示的に構成されていない場合にクライアントとサーバーが使用するデフォルトの TLS 暗号スイートを変更し、3DES 暗号スイートを削除しました。デフォルトは、tls3des 設定を使用して元に戻すことができます。

Go 1.23 は、tls.X509KeyPair および tls.LoadX509KeyPair の動作を変更し、返される tls.Certificate の Leaf フィールドを埋めるようにしました。この動作は x509keypairleaf 設定によって制御されます。Go 1.23 では、x509keypairleaf=1 がデフォルトです。以前のバージョンでは x509keypairleaf=0 がデフォルトでした。

Go 1.23 は、エラーを処理する際に Cache-Control、Content-Encoding、Etag、および Last-Modified ヘッダーを削除するように net/http.ServeContentnet/http.ServeFile、および net/http.ServeFS を変更しました。この動作は、httpservecontentkeepheaders 設定によって制御されます。httpservecontentkeepheaders=1 を使用すると、Go 1.23 以前の動作に戻ります。

Go 1.22

Go 1.22 は、TLS ハンドシェイクで使用できる許容される最大 RSA キーサイズを制御する構成可能な制限を追加しました。これは tlsmaxrsasize 設定によって制御されます。デフォルトは tlsmaxrsasize=8192 で、RSA を 8192 ビットキーに制限します。サービス拒否攻撃を回避するため、この設定とデフォルトは Go 1.19.13、Go 1.20.8、および Go 1.21.1 にバックポートされました。

Go 1.22 は、net/http クライアントまたはサーバーによって読み取られたリクエストまたはレスポンスに空の Content-Length ヘッダーがあることをエラーとしました。この動作は httplaxcontentlength 設定によって制御されます。

Go 1.22 は、ServeMux の動作を拡張パターンを受け入れ、パターンとリクエストパスの両方をセグメントごとにアンエスケープするように変更しました。この動作は、httpmuxgo121 設定によって制御できます。

Go 1.22 は、型エイリアスを明示的に表現するために、go/typesAlias 型を追加しました。型チェッカーが Alias 型を生成するかどうかは、gotypesalias 設定によって制御されます。Go 1.22 では gotypesalias=0 がデフォルトです。Go 1.23 では、gotypesalias=1 がデフォルトになります。この設定は将来のリリース (最短で Go 1.27) で削除されます。

Go 1.22 は、サーバーとクライアントの両方でサポートされるデフォルトの最小 TLS バージョンを TLS 1.2 に変更しました。デフォルトは、tls10server 設定を使用して TLS 1.0 に戻すことができます。

Go 1.22 は、明示的に構成されていない場合にクライアントとサーバーが使用するデフォルトの TLS 暗号スイートを変更し、RSA ベースのキー交換を使用する暗号スイートを削除しました。デフォルトは、tlsrsakex 設定を使用して元に戻すことができます。

Go 1.22 は、接続が TLS 1.3 または Extended Master Secret (Go 1.21 で実装) のいずれもサポートしない場合に ConnectionState.ExportKeyingMaterial を無効にしました。これは、tlsunsafeekm 設定で再度有効にすることができます。

Go 1.22 は、Linux 上の透過的巨大ページとのランタイムの相互作用を変更しました。特に、一般的なデフォルトの Linux カーネル構成は、かなりのメモリオーバーヘッドを引き起こす可能性があり、Go 1.22 はこのデフォルトを回避しなくなりました。カーネル設定を調整せずにこの問題を回避するには、disablethp 設定を使用して Go メモリの透過的巨大ページを無効にすることができます。この動作は Go 1.21.1 にバックポートされましたが、この設定は Go 1.21.6 以降でのみ利用可能です。この設定は将来のリリースで削除される可能性があり、この問題の影響を受けるユーザーは、GC ガイドの推奨事項に従って Linux 構成を調整するか、透過的巨大ページを完全に無効にする Linux ディストリビューションに切り替える必要があります。

Go 1.22 は、mutex プロファイルにランタイム内部ロックの競合を追加しました。これらのロックの競合は常に runtime._LostContendedRuntimeLock で報告されます。ランタイムロックの完全なスタックトレースは、runtimecontentionstacks 設定で有効にできます。これらのスタックトレースには非標準のセマンティクスがあります。詳細については、設定ドキュメントを参照してください。

Go 1.22 は、crypto/x509.Certificate フィールドに新しい Policies を追加しました。これは、31 ビットを超えるコンポーネントを持つ証明書ポリシー OID をサポートします。デフォルトでは、このフィールドは解析時にポリシー OID が設定されますが、マーシャリング時には使用されません。x509usepolicies 設定を使用すると、既存の PolicyIdentifiers フィールドの代わりに、これらのより大きな OID をマーシャリングするために使用できます。

Go 1.21

Go 1.21 は、nil インターフェース値で panic を呼び出すことをランタイムエラーとしました。これは panicnil 設定によって制御されます。

Go 1.21 は、html/template アクションが ECMAScript 6 テンプレートリテラル内に現れることをエラーとしました。これは jstmpllitinterp 設定によって制御されます。この動作は Go 1.19.8 以降および Go 1.20.3 以降にバックポートされました。

Go 1.21 は、MIME ヘッダーとマルチパートフォームの最大数に制限を導入しました。これはそれぞれ multipartmaxheaders および multipartmaxparts 設定によって制御されます。この動作は Go 1.19.8 以降および Go 1.20.3 以降にバックポートされました。

Go 1.21 は、マルチパス TCP のサポートを追加しましたが、アプリケーションが明示的に要求した場合にのみ使用されます。この動作は、multipathtcp 設定によって制御できます。

これらの設定を削除する計画はありません。

Go 1.20

Go 1.20 は、tar および zip アーカイブ内の安全でないパスを拒否するサポートを導入しました。これは tarinsecurepath 設定zipinsecurepath 設定によって制御されます。これらはデフォルトで tarinsecurepath=1 および zipinsecurepath=1 であり、以前のバージョンの Go の動作を維持します。将来のバージョンの Go では、デフォルトを tarinsecurepath=0 および zipinsecurepath=0 に変更する可能性があります。

Go 1.20 は、math/rand グローバル乱数ジェネレータの自動シードを導入しました。これは randautoseed 設定によって制御されます。

Go 1.20 は、証明書検証時に使用するフォールバックルートの概念を導入しました。これは x509usefallbackroots 設定によって制御されます。

Go 1.20 は、標準ライブラリのプリインストールされた .a ファイルを Go ディストリビューションから削除しました。インストールは、他のモジュールのパッケージのように標準ライブラリをビルドおよびキャッシュするようになりました。installgoroot 設定は、プリインストールされた .a ファイルのインストールと使用を復元します。

これらの設定を削除する計画はありません。

Go 1.19

Go 1.19 は、パスルックアップが現在のディレクトリ内のバイナリに解決されることをエラーとしました。これは execerrdot 設定によって制御されます。この設定を削除する計画はありません。

Go 1.19 は、DNS リクエストで EDNS0 追加ヘッダーを送信するようになりました。これにより、CenturyLink Zyxel C3000Z などの一部のルーターで提供される DNS サーバーが壊れる可能性があると報告されています。これは netedns0 設定によって変更できます。この設定は Go 1.21.12、Go 1.22.5、Go 1.23 以降で利用可能です。この設定を削除する計画はありません。

Go 1.18

Go 1.18 は、ほとんどの X.509 証明書での SHA1 のサポートを削除しました。これは x509sha1 設定によって制御されます。この設定は Go 1.24 で削除されました。

Go 1.10

Go 1.10 は、ビルドキャッシュの動作を変更し、テストキャッシュを追加しました。これには gocacheverifygocachehash、および gocachetest 設定が含まれます。これらの設定を削除する計画はありません。

Go 1.6

Go 1.6 は、HTTP/2 の透過的なサポートを導入しました。これは http2clienthttp2server、および http2debug 設定によって制御されます。これらの設定を削除する計画はありません。

Go 1.5

Go 1.5 は、純粋な Go DNS リゾルバーを導入しました。これは netdns 設定によって制御されます。この設定を削除する計画はありません。