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

はじめに

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

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

GODEBUG=http2client=0,http2server=0

そのGoプログラムは、HTTPクライアントとHTTPサーバーの両方でHTTP/2の使用をデフォルトで無効にします。特定のプログラムのデフォルトのGODEBUGを設定することも可能です(後述)。

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

互換性のために追加されたGODEBUG設定は、最低2年間(4つのGoリリース)維持されます。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はpanicnil設定を導入し、panic(nil)が許可されるかどうかを制御します。これは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 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行を拒否します。)

gogodebug行からのデフォルトは、ビルドされるすべてのメインパッケージに適用されます。より細かい制御を行うために、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ディレクティブを無効なプログラムとして扱います。特定の設定に対して複数の//go:debug行を持つプログラムも無効として扱われます。(古いツールチェーンは//go:debugディレクティブを完全に無視します。)

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

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

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

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

GODEBUG履歴

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

Go 1.23

Go 1.23では、package timeによって作成されるチャネルが非バッファード(同期)に変更されました。これにより、Timer.StopメソッドとTimer.Resetメソッドの結果の正しい使用方法がはるかに容易になります。asynctimerchan設定はこの変更を無効にします。この変更に関するランタイムメトリクスはありません。この設定は、将来のリリース(早ければGo 1.27)で削除される可能性があります。

Go 1.23では、リパー スポイントについてos.Lstatos.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.Readlinkfilepath.EvalSymlinksが、ボリュームをドライブ文字に正規化しようとしないように変更されました。これは常に可能だったわけではありません。この動作はwinreadlinkvolume設定で制御されます。Go 1.23では、デフォルトはwinreadlinkvolume=1です。以前のバージョンでは、デフォルトはwinreadlinkvolume=0でした。

Go 1.23では、実験的なポスト量子鍵交換メカニズムX25519Kyber768Draft00がデフォルトで有効になりました。デフォルトはtlskyber設定を使用して元に戻すことができます。

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.CertificateのLeafフィールドを設定するようにtls.X509KeyPairtls.LoadX509KeyPairの動作が変更されました。この動作はx509keypairleaf設定で制御されます。Go 1.23では、デフォルトはx509keypairleaf=1です。以前のバージョンでは、デフォルトはx509keypairleaf=0でした。

Go 1.23では、エラーの処理時にCache-Control、Content-Encoding、Etag、Last-Modifiedヘッダーを削除するようにnet/http.ServeContentnet/http.ServeFilenet/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と拡張マスターシークレット(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で設定されますが、マーシャリング中には使用されません。既存のPolicyIdentifiersフィールドの代わりに、これらのより大きなOIDをマーシャリングするために、x509usepolicies設定を使用できます。

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ヘッダーとマルチパートフォームの最大数の制限が導入されました。これはそれぞれmultipartmaxheadersmultipartmaxparts設定によって制御されます。この動作はGo 1.19.8+およびGo 1.20.3+にバックポートされました。

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

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

Go 1.20

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

Go 1.20では、math/randグローバル乱数生成器の自動シード生成が導入されました。これはrandautoseed設定によって制御されます。

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

Go 1.20では、Goディストリビューションから標準ライブラリのプリインストールされた.aファイルが削除されました。インストールでは、標準ライブラリは他のモジュールのパッケージのようにビルドおよびキャッシュされるようになりました。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.22以降)で削除される予定です。

Go 1.10

Go 1.10では、ビルドキャッシングの動作が変更され、テストキャッシングが追加されました。gocacheverifygocachehashgocachetest設定も追加されました。これらの設定を削除する予定はありません。

Go 1.6

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

Go 1.5

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